162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Landlock tests - Filesystem 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 662306a36Sopenharmony_ci * Copyright © 2020 ANSSI 762306a36Sopenharmony_ci * Copyright © 2020-2022 Microsoft Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define _GNU_SOURCE 1162306a36Sopenharmony_ci#include <fcntl.h> 1262306a36Sopenharmony_ci#include <linux/landlock.h> 1362306a36Sopenharmony_ci#include <linux/magic.h> 1462306a36Sopenharmony_ci#include <sched.h> 1562306a36Sopenharmony_ci#include <stdio.h> 1662306a36Sopenharmony_ci#include <string.h> 1762306a36Sopenharmony_ci#include <sys/capability.h> 1862306a36Sopenharmony_ci#include <sys/mount.h> 1962306a36Sopenharmony_ci#include <sys/prctl.h> 2062306a36Sopenharmony_ci#include <sys/sendfile.h> 2162306a36Sopenharmony_ci#include <sys/stat.h> 2262306a36Sopenharmony_ci#include <sys/sysmacros.h> 2362306a36Sopenharmony_ci#include <sys/vfs.h> 2462306a36Sopenharmony_ci#include <unistd.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "common.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#ifndef renameat2 2962306a36Sopenharmony_ciint renameat2(int olddirfd, const char *oldpath, int newdirfd, 3062306a36Sopenharmony_ci const char *newpath, unsigned int flags) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 3362306a36Sopenharmony_ci flags); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#ifndef RENAME_EXCHANGE 3862306a36Sopenharmony_ci#define RENAME_EXCHANGE (1 << 1) 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define TMP_DIR "tmp" 4262306a36Sopenharmony_ci#define BINARY_PATH "./true" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Paths (sibling number and depth) */ 4562306a36Sopenharmony_cistatic const char dir_s1d1[] = TMP_DIR "/s1d1"; 4662306a36Sopenharmony_cistatic const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 4762306a36Sopenharmony_cistatic const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 4862306a36Sopenharmony_cistatic const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 4962306a36Sopenharmony_cistatic const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 5062306a36Sopenharmony_cistatic const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 5162306a36Sopenharmony_cistatic const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 5262306a36Sopenharmony_cistatic const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 5362306a36Sopenharmony_cistatic const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const char dir_s2d1[] = TMP_DIR "/s2d1"; 5662306a36Sopenharmony_cistatic const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 5762306a36Sopenharmony_cistatic const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 5862306a36Sopenharmony_cistatic const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 5962306a36Sopenharmony_cistatic const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 6062306a36Sopenharmony_cistatic const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 6162306a36Sopenharmony_cistatic const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const char dir_s3d1[] = TMP_DIR "/s3d1"; 6462306a36Sopenharmony_cistatic const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; 6562306a36Sopenharmony_ci/* dir_s3d2 is a mount point. */ 6662306a36Sopenharmony_cistatic const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 6762306a36Sopenharmony_cistatic const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * layout1 hierarchy: 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * tmp 7362306a36Sopenharmony_ci * ├── s1d1 7462306a36Sopenharmony_ci * │ ├── f1 7562306a36Sopenharmony_ci * │ ├── f2 7662306a36Sopenharmony_ci * │ └── s1d2 7762306a36Sopenharmony_ci * │ ├── f1 7862306a36Sopenharmony_ci * │ ├── f2 7962306a36Sopenharmony_ci * │ └── s1d3 8062306a36Sopenharmony_ci * │ ├── f1 8162306a36Sopenharmony_ci * │ └── f2 8262306a36Sopenharmony_ci * ├── s2d1 8362306a36Sopenharmony_ci * │ ├── f1 8462306a36Sopenharmony_ci * │ └── s2d2 8562306a36Sopenharmony_ci * │ ├── f1 8662306a36Sopenharmony_ci * │ └── s2d3 8762306a36Sopenharmony_ci * │ ├── f1 8862306a36Sopenharmony_ci * │ └── f2 8962306a36Sopenharmony_ci * └── s3d1 9062306a36Sopenharmony_ci * ├── f1 9162306a36Sopenharmony_ci * └── s3d2 9262306a36Sopenharmony_ci * └── s3d3 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic bool fgrep(FILE *const inf, const char *const str) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci char line[32]; 9862306a36Sopenharmony_ci const int slen = strlen(str); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci while (!feof(inf)) { 10162306a36Sopenharmony_ci if (!fgets(line, sizeof(line), inf)) 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci if (strncmp(line, str, slen)) 10462306a36Sopenharmony_ci continue; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return true; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return false; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic bool supports_filesystem(const char *const filesystem) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci char str[32]; 11562306a36Sopenharmony_ci int len; 11662306a36Sopenharmony_ci bool res = true; 11762306a36Sopenharmony_ci FILE *const inf = fopen("/proc/filesystems", "r"); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* 12062306a36Sopenharmony_ci * Consider that the filesystem is supported if we cannot get the 12162306a36Sopenharmony_ci * supported ones. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci if (!inf) 12462306a36Sopenharmony_ci return true; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* filesystem can be null for bind mounts. */ 12762306a36Sopenharmony_ci if (!filesystem) 12862306a36Sopenharmony_ci goto out; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); 13162306a36Sopenharmony_ci if (len >= sizeof(str)) 13262306a36Sopenharmony_ci /* Ignores too-long filesystem names. */ 13362306a36Sopenharmony_ci goto out; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci res = fgrep(inf, str); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciout: 13862306a36Sopenharmony_ci fclose(inf); 13962306a36Sopenharmony_ci return res; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic bool cwd_matches_fs(unsigned int fs_magic) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct statfs statfs_buf; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!fs_magic) 14762306a36Sopenharmony_ci return true; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (statfs(".", &statfs_buf)) 15062306a36Sopenharmony_ci return true; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return statfs_buf.f_type == fs_magic; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void mkdir_parents(struct __test_metadata *const _metadata, 15662306a36Sopenharmony_ci const char *const path) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci char *walker; 15962306a36Sopenharmony_ci const char *parent; 16062306a36Sopenharmony_ci int i, err; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ASSERT_NE(path[0], '\0'); 16362306a36Sopenharmony_ci walker = strdup(path); 16462306a36Sopenharmony_ci ASSERT_NE(NULL, walker); 16562306a36Sopenharmony_ci parent = walker; 16662306a36Sopenharmony_ci for (i = 1; walker[i]; i++) { 16762306a36Sopenharmony_ci if (walker[i] != '/') 16862306a36Sopenharmony_ci continue; 16962306a36Sopenharmony_ci walker[i] = '\0'; 17062306a36Sopenharmony_ci err = mkdir(parent, 0700); 17162306a36Sopenharmony_ci ASSERT_FALSE(err && errno != EEXIST) 17262306a36Sopenharmony_ci { 17362306a36Sopenharmony_ci TH_LOG("Failed to create directory \"%s\": %s", parent, 17462306a36Sopenharmony_ci strerror(errno)); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci walker[i] = '/'; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci free(walker); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void create_directory(struct __test_metadata *const _metadata, 18262306a36Sopenharmony_ci const char *const path) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci mkdir_parents(_metadata, path); 18562306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(path, 0700)) 18662306a36Sopenharmony_ci { 18762306a36Sopenharmony_ci TH_LOG("Failed to create directory \"%s\": %s", path, 18862306a36Sopenharmony_ci strerror(errno)); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void create_file(struct __test_metadata *const _metadata, 19362306a36Sopenharmony_ci const char *const path) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci mkdir_parents(_metadata, path); 19662306a36Sopenharmony_ci ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 19762306a36Sopenharmony_ci { 19862306a36Sopenharmony_ci TH_LOG("Failed to create file \"%s\": %s", path, 19962306a36Sopenharmony_ci strerror(errno)); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int remove_path(const char *const path) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci char *walker; 20662306a36Sopenharmony_ci int i, ret, err = 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci walker = strdup(path); 20962306a36Sopenharmony_ci if (!walker) { 21062306a36Sopenharmony_ci err = ENOMEM; 21162306a36Sopenharmony_ci goto out; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci if (unlink(path) && rmdir(path)) { 21462306a36Sopenharmony_ci if (errno != ENOENT && errno != ENOTDIR) 21562306a36Sopenharmony_ci err = errno; 21662306a36Sopenharmony_ci goto out; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci for (i = strlen(walker); i > 0; i--) { 21962306a36Sopenharmony_ci if (walker[i] != '/') 22062306a36Sopenharmony_ci continue; 22162306a36Sopenharmony_ci walker[i] = '\0'; 22262306a36Sopenharmony_ci ret = rmdir(walker); 22362306a36Sopenharmony_ci if (ret) { 22462306a36Sopenharmony_ci if (errno != ENOTEMPTY && errno != EBUSY) 22562306a36Sopenharmony_ci err = errno; 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci if (strcmp(walker, TMP_DIR) == 0) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ciout: 23362306a36Sopenharmony_ci free(walker); 23462306a36Sopenharmony_ci return err; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistruct mnt_opt { 23862306a36Sopenharmony_ci const char *const source; 23962306a36Sopenharmony_ci const char *const type; 24062306a36Sopenharmony_ci const unsigned long flags; 24162306a36Sopenharmony_ci const char *const data; 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#define MNT_TMP_DATA "size=4m,mode=700" 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct mnt_opt mnt_tmp = { 24762306a36Sopenharmony_ci .type = "tmpfs", 24862306a36Sopenharmony_ci .data = MNT_TMP_DATA, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int mount_opt(const struct mnt_opt *const mnt, const char *const target) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags, 25462306a36Sopenharmony_ci mnt->data); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void prepare_layout_opt(struct __test_metadata *const _metadata, 25862306a36Sopenharmony_ci const struct mnt_opt *const mnt) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci disable_caps(_metadata); 26162306a36Sopenharmony_ci umask(0077); 26262306a36Sopenharmony_ci create_directory(_metadata, TMP_DIR); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* 26562306a36Sopenharmony_ci * Do not pollute the rest of the system: creates a private mount point 26662306a36Sopenharmony_ci * for tests relying on pivot_root(2) and move_mount(2). 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 26962306a36Sopenharmony_ci ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); 27062306a36Sopenharmony_ci ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) 27162306a36Sopenharmony_ci { 27262306a36Sopenharmony_ci TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, 27362306a36Sopenharmony_ci strerror(errno)); 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() 27662306a36Sopenharmony_ci * failed, so we need to explicitly do a minimal cleanup to 27762306a36Sopenharmony_ci * avoid cascading errors with other tests that don't depend on 27862306a36Sopenharmony_ci * the same filesystem. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci remove_path(TMP_DIR); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 28362306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void prepare_layout(struct __test_metadata *const _metadata) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci prepare_layout_opt(_metadata, &mnt_tmp); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void cleanup_layout(struct __test_metadata *const _metadata) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 29462306a36Sopenharmony_ci EXPECT_EQ(0, umount(TMP_DIR)); 29562306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 29662306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(TMP_DIR)); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* clang-format off */ 30062306a36Sopenharmony_ciFIXTURE(layout0) {}; 30162306a36Sopenharmony_ci/* clang-format on */ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciFIXTURE_SETUP(layout0) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci prepare_layout(_metadata); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout0) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci cleanup_layout(_metadata); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic void create_layout1(struct __test_metadata *const _metadata) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci create_file(_metadata, file1_s1d1); 31662306a36Sopenharmony_ci create_file(_metadata, file1_s1d2); 31762306a36Sopenharmony_ci create_file(_metadata, file1_s1d3); 31862306a36Sopenharmony_ci create_file(_metadata, file2_s1d1); 31962306a36Sopenharmony_ci create_file(_metadata, file2_s1d2); 32062306a36Sopenharmony_ci create_file(_metadata, file2_s1d3); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci create_file(_metadata, file1_s2d1); 32362306a36Sopenharmony_ci create_file(_metadata, file1_s2d2); 32462306a36Sopenharmony_ci create_file(_metadata, file1_s2d3); 32562306a36Sopenharmony_ci create_file(_metadata, file2_s2d3); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci create_file(_metadata, file1_s3d1); 32862306a36Sopenharmony_ci create_directory(_metadata, dir_s3d2); 32962306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 33062306a36Sopenharmony_ci ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 33162306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void remove_layout1(struct __test_metadata *const _metadata) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file2_s1d3)); 33962306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file2_s1d2)); 34062306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file2_s1d1)); 34162306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s1d3)); 34262306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s1d2)); 34362306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s1d1)); 34462306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(dir_s1d3)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file2_s2d3)); 34762306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s2d3)); 34862306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s2d2)); 34962306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s2d1)); 35062306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(dir_s2d2)); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s3d1)); 35362306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(dir_s3d3)); 35462306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 35562306a36Sopenharmony_ci umount(dir_s3d2); 35662306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 35762306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(dir_s3d2)); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* clang-format off */ 36162306a36Sopenharmony_ciFIXTURE(layout1) {}; 36262306a36Sopenharmony_ci/* clang-format on */ 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ciFIXTURE_SETUP(layout1) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci prepare_layout(_metadata); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci create_layout1(_metadata); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout1) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci remove_layout1(_metadata); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci cleanup_layout(_metadata); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* 37962306a36Sopenharmony_ci * This helper enables to use the ASSERT_* macros and print the line number 38062306a36Sopenharmony_ci * pointing to the test caller. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_cistatic int test_open_rel(const int dirfd, const char *const path, 38362306a36Sopenharmony_ci const int flags) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci int fd; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Works with file and directories. */ 38862306a36Sopenharmony_ci fd = openat(dirfd, path, flags | O_CLOEXEC); 38962306a36Sopenharmony_ci if (fd < 0) 39062306a36Sopenharmony_ci return errno; 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci * Mixing error codes from close(2) and open(2) should not lead to any 39362306a36Sopenharmony_ci * (access type) confusion for this test. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci if (close(fd) != 0) 39662306a36Sopenharmony_ci return errno; 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int test_open(const char *const path, const int flags) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci return test_open_rel(AT_FDCWD, path, flags); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciTEST_F_FORK(layout1, no_restriction) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 40862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 40962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 41062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 41162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 41262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 41362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 41462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 41762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 41862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 41962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 42062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 42162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 42462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 42562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciTEST_F_FORK(layout1, inval) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct landlock_path_beneath_attr path_beneath = { 43162306a36Sopenharmony_ci .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 43262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 43362306a36Sopenharmony_ci .parent_fd = -1, 43462306a36Sopenharmony_ci }; 43562306a36Sopenharmony_ci struct landlock_ruleset_attr ruleset_attr = { 43662306a36Sopenharmony_ci .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 43762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 43862306a36Sopenharmony_ci }; 43962306a36Sopenharmony_ci int ruleset_fd; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci path_beneath.parent_fd = 44262306a36Sopenharmony_ci open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 44362306a36Sopenharmony_ci ASSERT_LE(0, path_beneath.parent_fd); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 44662306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 44762306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 44862306a36Sopenharmony_ci &path_beneath, 0)); 44962306a36Sopenharmony_ci /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 45062306a36Sopenharmony_ci ASSERT_EQ(EBADF, errno); 45162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 45462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 45562306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 45662306a36Sopenharmony_ci &path_beneath, 0)); 45762306a36Sopenharmony_ci /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 45862306a36Sopenharmony_ci ASSERT_EQ(EBADFD, errno); 45962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Gets a real ruleset. */ 46262306a36Sopenharmony_ci ruleset_fd = 46362306a36Sopenharmony_ci landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 46462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 46562306a36Sopenharmony_ci ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 46662306a36Sopenharmony_ci &path_beneath, 0)); 46762306a36Sopenharmony_ci ASSERT_EQ(0, close(path_beneath.parent_fd)); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Tests without O_PATH. */ 47062306a36Sopenharmony_ci path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 47162306a36Sopenharmony_ci ASSERT_LE(0, path_beneath.parent_fd); 47262306a36Sopenharmony_ci ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 47362306a36Sopenharmony_ci &path_beneath, 0)); 47462306a36Sopenharmony_ci ASSERT_EQ(0, close(path_beneath.parent_fd)); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Tests with a ruleset FD. */ 47762306a36Sopenharmony_ci path_beneath.parent_fd = ruleset_fd; 47862306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 47962306a36Sopenharmony_ci &path_beneath, 0)); 48062306a36Sopenharmony_ci ASSERT_EQ(EBADFD, errno); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Checks unhandled allowed_access. */ 48362306a36Sopenharmony_ci path_beneath.parent_fd = 48462306a36Sopenharmony_ci open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 48562306a36Sopenharmony_ci ASSERT_LE(0, path_beneath.parent_fd); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Test with legitimate values. */ 48862306a36Sopenharmony_ci path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 48962306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 49062306a36Sopenharmony_ci &path_beneath, 0)); 49162306a36Sopenharmony_ci ASSERT_EQ(EINVAL, errno); 49262306a36Sopenharmony_ci path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Tests with denied-by-default access right. */ 49562306a36Sopenharmony_ci path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 49662306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 49762306a36Sopenharmony_ci &path_beneath, 0)); 49862306a36Sopenharmony_ci ASSERT_EQ(EINVAL, errno); 49962306a36Sopenharmony_ci path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Test with unknown (64-bits) value. */ 50262306a36Sopenharmony_ci path_beneath.allowed_access |= (1ULL << 60); 50362306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 50462306a36Sopenharmony_ci &path_beneath, 0)); 50562306a36Sopenharmony_ci ASSERT_EQ(EINVAL, errno); 50662306a36Sopenharmony_ci path_beneath.allowed_access &= ~(1ULL << 60); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Test with no access. */ 50962306a36Sopenharmony_ci path_beneath.allowed_access = 0; 51062306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 51162306a36Sopenharmony_ci &path_beneath, 0)); 51262306a36Sopenharmony_ci ASSERT_EQ(ENOMSG, errno); 51362306a36Sopenharmony_ci path_beneath.allowed_access &= ~(1ULL << 60); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ASSERT_EQ(0, close(path_beneath.parent_fd)); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Enforces the ruleset. */ 51862306a36Sopenharmony_ci ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 51962306a36Sopenharmony_ci ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/* clang-format off */ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci#define ACCESS_FILE ( \ 52762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_EXECUTE | \ 52862306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE | \ 52962306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | \ 53062306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_TRUNCATE) 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci#define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci#define ACCESS_ALL ( \ 53562306a36Sopenharmony_ci ACCESS_FILE | \ 53662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_DIR | \ 53762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 53862306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 53962306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 54062306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_DIR | \ 54162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_REG | \ 54262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 54362306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 54462306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 54562306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_SYM | \ 54662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REFER) 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/* clang-format on */ 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ciTEST_F_FORK(layout1, file_and_dir_access_rights) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci __u64 access; 55362306a36Sopenharmony_ci int err; 55462306a36Sopenharmony_ci struct landlock_path_beneath_attr path_beneath_file = {}, 55562306a36Sopenharmony_ci path_beneath_dir = {}; 55662306a36Sopenharmony_ci struct landlock_ruleset_attr ruleset_attr = { 55762306a36Sopenharmony_ci .handled_access_fs = ACCESS_ALL, 55862306a36Sopenharmony_ci }; 55962306a36Sopenharmony_ci const int ruleset_fd = 56062306a36Sopenharmony_ci landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Tests access rights for files. */ 56562306a36Sopenharmony_ci path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 56662306a36Sopenharmony_ci ASSERT_LE(0, path_beneath_file.parent_fd); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* Tests access rights for directories. */ 56962306a36Sopenharmony_ci path_beneath_dir.parent_fd = 57062306a36Sopenharmony_ci open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 57162306a36Sopenharmony_ci ASSERT_LE(0, path_beneath_dir.parent_fd); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci for (access = 1; access <= ACCESS_LAST; access <<= 1) { 57462306a36Sopenharmony_ci path_beneath_dir.allowed_access = access; 57562306a36Sopenharmony_ci ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 57662306a36Sopenharmony_ci LANDLOCK_RULE_PATH_BENEATH, 57762306a36Sopenharmony_ci &path_beneath_dir, 0)); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci path_beneath_file.allowed_access = access; 58062306a36Sopenharmony_ci err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 58162306a36Sopenharmony_ci &path_beneath_file, 0); 58262306a36Sopenharmony_ci if (access & ACCESS_FILE) { 58362306a36Sopenharmony_ci ASSERT_EQ(0, err); 58462306a36Sopenharmony_ci } else { 58562306a36Sopenharmony_ci ASSERT_EQ(-1, err); 58662306a36Sopenharmony_ci ASSERT_EQ(EINVAL, errno); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 59062306a36Sopenharmony_ci ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 59162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ciTEST_F_FORK(layout0, unknown_access_rights) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci __u64 access_mask; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 59962306a36Sopenharmony_ci access_mask >>= 1) { 60062306a36Sopenharmony_ci struct landlock_ruleset_attr ruleset_attr = { 60162306a36Sopenharmony_ci .handled_access_fs = access_mask, 60262306a36Sopenharmony_ci }; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 60562306a36Sopenharmony_ci sizeof(ruleset_attr), 0)); 60662306a36Sopenharmony_ci ASSERT_EQ(EINVAL, errno); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void add_path_beneath(struct __test_metadata *const _metadata, 61162306a36Sopenharmony_ci const int ruleset_fd, const __u64 allowed_access, 61262306a36Sopenharmony_ci const char *const path) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct landlock_path_beneath_attr path_beneath = { 61562306a36Sopenharmony_ci .allowed_access = allowed_access, 61662306a36Sopenharmony_ci }; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 61962306a36Sopenharmony_ci ASSERT_LE(0, path_beneath.parent_fd) 62062306a36Sopenharmony_ci { 62162306a36Sopenharmony_ci TH_LOG("Failed to open directory \"%s\": %s", path, 62262306a36Sopenharmony_ci strerror(errno)); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 62562306a36Sopenharmony_ci &path_beneath, 0)) 62662306a36Sopenharmony_ci { 62762306a36Sopenharmony_ci TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 62862306a36Sopenharmony_ci strerror(errno)); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci ASSERT_EQ(0, close(path_beneath.parent_fd)); 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistruct rule { 63462306a36Sopenharmony_ci const char *path; 63562306a36Sopenharmony_ci __u64 access; 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/* clang-format off */ 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci#define ACCESS_RO ( \ 64162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | \ 64262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_DIR) 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci#define ACCESS_RW ( \ 64562306a36Sopenharmony_ci ACCESS_RO | \ 64662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE) 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci/* clang-format on */ 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic int create_ruleset(struct __test_metadata *const _metadata, 65162306a36Sopenharmony_ci const __u64 handled_access_fs, 65262306a36Sopenharmony_ci const struct rule rules[]) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci int ruleset_fd, i; 65562306a36Sopenharmony_ci struct landlock_ruleset_attr ruleset_attr = { 65662306a36Sopenharmony_ci .handled_access_fs = handled_access_fs, 65762306a36Sopenharmony_ci }; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci ASSERT_NE(NULL, rules) 66062306a36Sopenharmony_ci { 66162306a36Sopenharmony_ci TH_LOG("No rule list"); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci ASSERT_NE(NULL, rules[0].path) 66462306a36Sopenharmony_ci { 66562306a36Sopenharmony_ci TH_LOG("Empty rule list"); 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ruleset_fd = 66962306a36Sopenharmony_ci landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 67062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd) 67162306a36Sopenharmony_ci { 67262306a36Sopenharmony_ci TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci for (i = 0; rules[i].path; i++) { 67662306a36Sopenharmony_ci add_path_beneath(_metadata, ruleset_fd, rules[i].access, 67762306a36Sopenharmony_ci rules[i].path); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci return ruleset_fd; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic void enforce_ruleset(struct __test_metadata *const _metadata, 68362306a36Sopenharmony_ci const int ruleset_fd) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 68662306a36Sopenharmony_ci ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 68762306a36Sopenharmony_ci { 68862306a36Sopenharmony_ci TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciTEST_F_FORK(layout0, proc_nsfs) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci const struct rule rules[] = { 69562306a36Sopenharmony_ci { 69662306a36Sopenharmony_ci .path = "/dev/null", 69762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 69862306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 69962306a36Sopenharmony_ci }, 70062306a36Sopenharmony_ci {}, 70162306a36Sopenharmony_ci }; 70262306a36Sopenharmony_ci struct landlock_path_beneath_attr path_beneath; 70362306a36Sopenharmony_ci const int ruleset_fd = create_ruleset( 70462306a36Sopenharmony_ci _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 70562306a36Sopenharmony_ci rules); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 70862306a36Sopenharmony_ci ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 71362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 71462306a36Sopenharmony_ci ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 71562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 71862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 71962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 72062306a36Sopenharmony_ci /* 72162306a36Sopenharmony_ci * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 72262306a36Sopenharmony_ci * disconnected path. Such path cannot be identified and must then be 72362306a36Sopenharmony_ci * allowed. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* 72862306a36Sopenharmony_ci * Checks that it is not possible to add nsfs-like filesystem 72962306a36Sopenharmony_ci * references to a ruleset. 73062306a36Sopenharmony_ci */ 73162306a36Sopenharmony_ci path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 73262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 73362306a36Sopenharmony_ci path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 73462306a36Sopenharmony_ci ASSERT_LE(0, path_beneath.parent_fd); 73562306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 73662306a36Sopenharmony_ci &path_beneath, 0)); 73762306a36Sopenharmony_ci ASSERT_EQ(EBADFD, errno); 73862306a36Sopenharmony_ci ASSERT_EQ(0, close(path_beneath.parent_fd)); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ciTEST_F_FORK(layout0, unpriv) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci const struct rule rules[] = { 74462306a36Sopenharmony_ci { 74562306a36Sopenharmony_ci .path = TMP_DIR, 74662306a36Sopenharmony_ci .access = ACCESS_RO, 74762306a36Sopenharmony_ci }, 74862306a36Sopenharmony_ci {}, 74962306a36Sopenharmony_ci }; 75062306a36Sopenharmony_ci int ruleset_fd; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci drop_caps(_metadata); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 75562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 75662306a36Sopenharmony_ci ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 75762306a36Sopenharmony_ci ASSERT_EQ(EPERM, errno); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* enforce_ruleset() calls prctl(no_new_privs). */ 76062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 76162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ciTEST_F_FORK(layout1, effective_access) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci const struct rule rules[] = { 76762306a36Sopenharmony_ci { 76862306a36Sopenharmony_ci .path = dir_s1d2, 76962306a36Sopenharmony_ci .access = ACCESS_RO, 77062306a36Sopenharmony_ci }, 77162306a36Sopenharmony_ci { 77262306a36Sopenharmony_ci .path = file1_s2d2, 77362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 77462306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 77562306a36Sopenharmony_ci }, 77662306a36Sopenharmony_ci {}, 77762306a36Sopenharmony_ci }; 77862306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 77962306a36Sopenharmony_ci char buf; 78062306a36Sopenharmony_ci int reg_fd; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 78362306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 78462306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Tests on a directory (with or without O_PATH). */ 78762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 78862306a36Sopenharmony_ci ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 78962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 79062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 79162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 79262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 79562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 79662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 79762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* Tests on a file (with or without O_PATH). */ 80062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 80162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* Checks effective read and write actions. */ 80662306a36Sopenharmony_ci reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 80762306a36Sopenharmony_ci ASSERT_LE(0, reg_fd); 80862306a36Sopenharmony_ci ASSERT_EQ(1, write(reg_fd, ".", 1)); 80962306a36Sopenharmony_ci ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 81062306a36Sopenharmony_ci ASSERT_EQ(1, read(reg_fd, &buf, 1)); 81162306a36Sopenharmony_ci ASSERT_EQ('.', buf); 81262306a36Sopenharmony_ci ASSERT_EQ(0, close(reg_fd)); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* Just in case, double-checks effective actions. */ 81562306a36Sopenharmony_ci reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 81662306a36Sopenharmony_ci ASSERT_LE(0, reg_fd); 81762306a36Sopenharmony_ci ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 81862306a36Sopenharmony_ci ASSERT_EQ(EBADF, errno); 81962306a36Sopenharmony_ci ASSERT_EQ(0, close(reg_fd)); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ciTEST_F_FORK(layout1, unhandled_access) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci const struct rule rules[] = { 82562306a36Sopenharmony_ci { 82662306a36Sopenharmony_ci .path = dir_s1d2, 82762306a36Sopenharmony_ci .access = ACCESS_RO, 82862306a36Sopenharmony_ci }, 82962306a36Sopenharmony_ci {}, 83062306a36Sopenharmony_ci }; 83162306a36Sopenharmony_ci /* Here, we only handle read accesses, not write accesses. */ 83262306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 83562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 83662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 84062306a36Sopenharmony_ci * opening for write-only should be allowed, but not read-write. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 84362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 84662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ciTEST_F_FORK(layout1, ruleset_overlap) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci const struct rule rules[] = { 85262306a36Sopenharmony_ci /* These rules should be ORed among them. */ 85362306a36Sopenharmony_ci { 85462306a36Sopenharmony_ci .path = dir_s1d2, 85562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 85662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 85762306a36Sopenharmony_ci }, 85862306a36Sopenharmony_ci { 85962306a36Sopenharmony_ci .path = dir_s1d2, 86062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 86162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_DIR, 86262306a36Sopenharmony_ci }, 86362306a36Sopenharmony_ci {}, 86462306a36Sopenharmony_ci }; 86562306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 86862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 86962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* Checks s1d1 hierarchy. */ 87262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 87362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 87462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 87562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Checks s1d2 hierarchy. */ 87862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 87962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 88062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 88162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* Checks s1d3 hierarchy. */ 88462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 88562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 88662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 88762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ciTEST_F_FORK(layout1, layer_rule_unions) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci const struct rule layer1[] = { 89362306a36Sopenharmony_ci { 89462306a36Sopenharmony_ci .path = dir_s1d2, 89562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 89662306a36Sopenharmony_ci }, 89762306a36Sopenharmony_ci /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 89862306a36Sopenharmony_ci { 89962306a36Sopenharmony_ci .path = dir_s1d3, 90062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 90162306a36Sopenharmony_ci }, 90262306a36Sopenharmony_ci {}, 90362306a36Sopenharmony_ci }; 90462306a36Sopenharmony_ci const struct rule layer2[] = { 90562306a36Sopenharmony_ci /* Doesn't change anything from layer1. */ 90662306a36Sopenharmony_ci { 90762306a36Sopenharmony_ci .path = dir_s1d2, 90862306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 90962306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 91062306a36Sopenharmony_ci }, 91162306a36Sopenharmony_ci {}, 91262306a36Sopenharmony_ci }; 91362306a36Sopenharmony_ci const struct rule layer3[] = { 91462306a36Sopenharmony_ci /* Only allows write (but not read) to dir_s1d3. */ 91562306a36Sopenharmony_ci { 91662306a36Sopenharmony_ci .path = dir_s1d2, 91762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 91862306a36Sopenharmony_ci }, 91962306a36Sopenharmony_ci {}, 92062306a36Sopenharmony_ci }; 92162306a36Sopenharmony_ci int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 92462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 92562306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* Checks s1d1 hierarchy with layer1. */ 92862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 92962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 93062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 93162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* Checks s1d2 hierarchy with layer1. */ 93462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 93562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 93662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 93762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Checks s1d3 hierarchy with layer1. */ 94062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 94162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 94262306a36Sopenharmony_ci /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 94362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 94462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* Doesn't change anything from layer1. */ 94762306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2); 94862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 94962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 95062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* Checks s1d1 hierarchy with layer2. */ 95362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 95462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 95562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 95662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* Checks s1d2 hierarchy with layer2. */ 95962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 96062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 96162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 96262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Checks s1d3 hierarchy with layer2. */ 96562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 96662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 96762306a36Sopenharmony_ci /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 96862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 96962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Only allows write (but not read) to dir_s1d3. */ 97262306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3); 97362306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 97462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 97562306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* Checks s1d1 hierarchy with layer3. */ 97862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 97962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 98062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 98162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Checks s1d2 hierarchy with layer3. */ 98462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 98562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 98662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 98762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* Checks s1d3 hierarchy with layer3. */ 99062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 99162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 99262306a36Sopenharmony_ci /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 99362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 99462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ciTEST_F_FORK(layout1, non_overlapping_accesses) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci const struct rule layer1[] = { 100062306a36Sopenharmony_ci { 100162306a36Sopenharmony_ci .path = dir_s1d2, 100262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 100362306a36Sopenharmony_ci }, 100462306a36Sopenharmony_ci {}, 100562306a36Sopenharmony_ci }; 100662306a36Sopenharmony_ci const struct rule layer2[] = { 100762306a36Sopenharmony_ci { 100862306a36Sopenharmony_ci .path = dir_s1d3, 100962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 101062306a36Sopenharmony_ci }, 101162306a36Sopenharmony_ci {}, 101262306a36Sopenharmony_ci }; 101362306a36Sopenharmony_ci int ruleset_fd; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 101662306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci ruleset_fd = 101962306a36Sopenharmony_ci create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 102062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 102162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 102262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 102562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 102662306a36Sopenharmony_ci ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 102762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 103062306a36Sopenharmony_ci layer2); 103162306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 103262306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 103362306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* Unchanged accesses for file creation. */ 103662306a36Sopenharmony_ci ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 103762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 103862306a36Sopenharmony_ci ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* Checks file removing. */ 104162306a36Sopenharmony_ci ASSERT_EQ(-1, unlink(file1_s1d2)); 104262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 104362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ciTEST_F_FORK(layout1, interleaved_masked_accesses) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci /* 104962306a36Sopenharmony_ci * Checks overly restrictive rules: 105062306a36Sopenharmony_ci * layer 1: allows R s1d1/s1d2/s1d3/file1 105162306a36Sopenharmony_ci * layer 2: allows RW s1d1/s1d2/s1d3 105262306a36Sopenharmony_ci * allows W s1d1/s1d2 105362306a36Sopenharmony_ci * denies R s1d1/s1d2 105462306a36Sopenharmony_ci * layer 3: allows R s1d1 105562306a36Sopenharmony_ci * layer 4: allows R s1d1/s1d2 105662306a36Sopenharmony_ci * denies W s1d1/s1d2 105762306a36Sopenharmony_ci * layer 5: allows R s1d1/s1d2 105862306a36Sopenharmony_ci * layer 6: allows X ---- 105962306a36Sopenharmony_ci * layer 7: allows W s1d1/s1d2 106062306a36Sopenharmony_ci * denies R s1d1/s1d2 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_ci const struct rule layer1_read[] = { 106362306a36Sopenharmony_ci /* Allows read access to file1_s1d3 with the first layer. */ 106462306a36Sopenharmony_ci { 106562306a36Sopenharmony_ci .path = file1_s1d3, 106662306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 106762306a36Sopenharmony_ci }, 106862306a36Sopenharmony_ci {}, 106962306a36Sopenharmony_ci }; 107062306a36Sopenharmony_ci /* First rule with write restrictions. */ 107162306a36Sopenharmony_ci const struct rule layer2_read_write[] = { 107262306a36Sopenharmony_ci /* Start by granting read-write access via its parent directory... */ 107362306a36Sopenharmony_ci { 107462306a36Sopenharmony_ci .path = dir_s1d3, 107562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 107662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 107762306a36Sopenharmony_ci }, 107862306a36Sopenharmony_ci /* ...but also denies read access via its grandparent directory. */ 107962306a36Sopenharmony_ci { 108062306a36Sopenharmony_ci .path = dir_s1d2, 108162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 108262306a36Sopenharmony_ci }, 108362306a36Sopenharmony_ci {}, 108462306a36Sopenharmony_ci }; 108562306a36Sopenharmony_ci const struct rule layer3_read[] = { 108662306a36Sopenharmony_ci /* Allows read access via its great-grandparent directory. */ 108762306a36Sopenharmony_ci { 108862306a36Sopenharmony_ci .path = dir_s1d1, 108962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 109062306a36Sopenharmony_ci }, 109162306a36Sopenharmony_ci {}, 109262306a36Sopenharmony_ci }; 109362306a36Sopenharmony_ci const struct rule layer4_read_write[] = { 109462306a36Sopenharmony_ci /* 109562306a36Sopenharmony_ci * Try to confuse the deny access by denying write (but not 109662306a36Sopenharmony_ci * read) access via its grandparent directory. 109762306a36Sopenharmony_ci */ 109862306a36Sopenharmony_ci { 109962306a36Sopenharmony_ci .path = dir_s1d2, 110062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 110162306a36Sopenharmony_ci }, 110262306a36Sopenharmony_ci {}, 110362306a36Sopenharmony_ci }; 110462306a36Sopenharmony_ci const struct rule layer5_read[] = { 110562306a36Sopenharmony_ci /* 110662306a36Sopenharmony_ci * Try to override layer2's deny read access by explicitly 110762306a36Sopenharmony_ci * allowing read access via file1_s1d3's grandparent. 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ci { 111062306a36Sopenharmony_ci .path = dir_s1d2, 111162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 111262306a36Sopenharmony_ci }, 111362306a36Sopenharmony_ci {}, 111462306a36Sopenharmony_ci }; 111562306a36Sopenharmony_ci const struct rule layer6_execute[] = { 111662306a36Sopenharmony_ci /* 111762306a36Sopenharmony_ci * Restricts an unrelated file hierarchy with a new access 111862306a36Sopenharmony_ci * (non-overlapping) type. 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_ci { 112162306a36Sopenharmony_ci .path = dir_s2d1, 112262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_EXECUTE, 112362306a36Sopenharmony_ci }, 112462306a36Sopenharmony_ci {}, 112562306a36Sopenharmony_ci }; 112662306a36Sopenharmony_ci const struct rule layer7_read_write[] = { 112762306a36Sopenharmony_ci /* 112862306a36Sopenharmony_ci * Finally, denies read access to file1_s1d3 via its 112962306a36Sopenharmony_ci * grandparent. 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_ci { 113262306a36Sopenharmony_ci .path = dir_s1d2, 113362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 113462306a36Sopenharmony_ci }, 113562306a36Sopenharmony_ci {}, 113662306a36Sopenharmony_ci }; 113762306a36Sopenharmony_ci int ruleset_fd; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 114062306a36Sopenharmony_ci layer1_read); 114162306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 114262306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 114362306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* Checks that read access is granted for file1_s1d3 with layer 1. */ 114662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 114762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 114862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, 115162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | 115262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 115362306a36Sopenharmony_ci layer2_read_write); 115462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 115562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 115662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* Checks that previous access rights are unchanged with layer 2. */ 115962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 116062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 116162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 116462306a36Sopenharmony_ci layer3_read); 116562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 116662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 116762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Checks that previous access rights are unchanged with layer 3. */ 117062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 117162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 117262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci /* This time, denies write access for the file hierarchy. */ 117562306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, 117662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | 117762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 117862306a36Sopenharmony_ci layer4_read_write); 117962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 118062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 118162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* 118462306a36Sopenharmony_ci * Checks that the only change with layer 4 is that write access is 118562306a36Sopenharmony_ci * denied. 118662306a36Sopenharmony_ci */ 118762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 118862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 118962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 119062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 119362306a36Sopenharmony_ci layer5_read); 119462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 119562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 119662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* Checks that previous access rights are unchanged with layer 5. */ 119962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 120062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 120162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 120262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 120562306a36Sopenharmony_ci layer6_execute); 120662306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 120762306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 120862306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci /* Checks that previous access rights are unchanged with layer 6. */ 121162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 121262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 121362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 121462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, 121762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | 121862306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 121962306a36Sopenharmony_ci layer7_read_write); 122062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 122162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 122262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* Checks read access is now denied with layer 7. */ 122562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 122662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 122762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 122862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ciTEST_F_FORK(layout1, inherit_subset) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci const struct rule rules[] = { 123462306a36Sopenharmony_ci { 123562306a36Sopenharmony_ci .path = dir_s1d2, 123662306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 123762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_DIR, 123862306a36Sopenharmony_ci }, 123962306a36Sopenharmony_ci {}, 124062306a36Sopenharmony_ci }; 124162306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 124462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 124762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* Write access is forbidden. */ 125062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 125162306a36Sopenharmony_ci /* Readdir access is allowed. */ 125262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci /* Write access is forbidden. */ 125562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 125662306a36Sopenharmony_ci /* Readdir access is allowed. */ 125762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* 126062306a36Sopenharmony_ci * Tests shared rule extension: the following rules should not grant 126162306a36Sopenharmony_ci * any new access, only remove some. Once enforced, these rules are 126262306a36Sopenharmony_ci * ANDed with the previous ones. 126362306a36Sopenharmony_ci */ 126462306a36Sopenharmony_ci add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 126562306a36Sopenharmony_ci dir_s1d2); 126662306a36Sopenharmony_ci /* 126762306a36Sopenharmony_ci * According to ruleset_fd, dir_s1d2 should now have the 126862306a36Sopenharmony_ci * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 126962306a36Sopenharmony_ci * access rights (even if this directory is opened a second time). 127062306a36Sopenharmony_ci * However, when enforcing this updated ruleset, the ruleset tied to 127162306a36Sopenharmony_ci * the current process (i.e. its domain) will still only have the 127262306a36Sopenharmony_ci * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 127362306a36Sopenharmony_ci * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 127462306a36Sopenharmony_ci * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 127562306a36Sopenharmony_ci * be a privilege escalation. 127662306a36Sopenharmony_ci */ 127762306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci /* Same tests and results as above. */ 128062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 128162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* It is still forbidden to write in file1_s1d2. */ 128462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 128562306a36Sopenharmony_ci /* Readdir access is still allowed. */ 128662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* It is still forbidden to write in file1_s1d3. */ 128962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 129062306a36Sopenharmony_ci /* Readdir access is still allowed. */ 129162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* 129462306a36Sopenharmony_ci * Try to get more privileges by adding new access rights to the parent 129562306a36Sopenharmony_ci * directory: dir_s1d1. 129662306a36Sopenharmony_ci */ 129762306a36Sopenharmony_ci add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 129862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* Same tests and results as above. */ 130162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 130262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci /* It is still forbidden to write in file1_s1d2. */ 130562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 130662306a36Sopenharmony_ci /* Readdir access is still allowed. */ 130762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci /* It is still forbidden to write in file1_s1d3. */ 131062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 131162306a36Sopenharmony_ci /* Readdir access is still allowed. */ 131262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* 131562306a36Sopenharmony_ci * Now, dir_s1d3 get a new rule tied to it, only allowing 131662306a36Sopenharmony_ci * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 131762306a36Sopenharmony_ci * that there was no rule tied to it before. 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_ci add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 132062306a36Sopenharmony_ci dir_s1d3); 132162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 132262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* 132562306a36Sopenharmony_ci * Same tests and results as above, except for open(dir_s1d3) which is 132662306a36Sopenharmony_ci * now denied because the new rule mask the rule previously inherited 132762306a36Sopenharmony_ci * from dir_s1d2. 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* Same tests and results as above. */ 133162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 133262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* It is still forbidden to write in file1_s1d2. */ 133562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 133662306a36Sopenharmony_ci /* Readdir access is still allowed. */ 133762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci /* It is still forbidden to write in file1_s1d3. */ 134062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 134162306a36Sopenharmony_ci /* 134262306a36Sopenharmony_ci * Readdir of dir_s1d3 is still allowed because of the OR policy inside 134362306a36Sopenharmony_ci * the same layer. 134462306a36Sopenharmony_ci */ 134562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ciTEST_F_FORK(layout1, inherit_superset) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci const struct rule rules[] = { 135162306a36Sopenharmony_ci { 135262306a36Sopenharmony_ci .path = dir_s1d3, 135362306a36Sopenharmony_ci .access = ACCESS_RO, 135462306a36Sopenharmony_ci }, 135562306a36Sopenharmony_ci {}, 135662306a36Sopenharmony_ci }; 135762306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 136062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci /* Readdir access is denied for dir_s1d2. */ 136362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 136462306a36Sopenharmony_ci /* Readdir access is allowed for dir_s1d3. */ 136562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 136662306a36Sopenharmony_ci /* File access is allowed for file1_s1d3. */ 136762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 137062306a36Sopenharmony_ci add_path_beneath(_metadata, ruleset_fd, 137162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | 137262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_DIR, 137362306a36Sopenharmony_ci dir_s1d2); 137462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 137562306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* Readdir access is still denied for dir_s1d2. */ 137862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 137962306a36Sopenharmony_ci /* Readdir access is still allowed for dir_s1d3. */ 138062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 138162306a36Sopenharmony_ci /* File access is still allowed for file1_s1d3. */ 138262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ciTEST_F_FORK(layout0, max_layers) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci int i, err; 138862306a36Sopenharmony_ci const struct rule rules[] = { 138962306a36Sopenharmony_ci { 139062306a36Sopenharmony_ci .path = TMP_DIR, 139162306a36Sopenharmony_ci .access = ACCESS_RO, 139262306a36Sopenharmony_ci }, 139362306a36Sopenharmony_ci {}, 139462306a36Sopenharmony_ci }; 139562306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 139862306a36Sopenharmony_ci for (i = 0; i < 16; i++) 139962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 140262306a36Sopenharmony_ci err = landlock_restrict_self(ruleset_fd, 0); 140362306a36Sopenharmony_ci ASSERT_EQ(-1, err); 140462306a36Sopenharmony_ci ASSERT_EQ(E2BIG, errno); 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ciTEST_F_FORK(layout1, empty_or_same_ruleset) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci struct landlock_ruleset_attr ruleset_attr = {}; 141262306a36Sopenharmony_ci int ruleset_fd; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* Tests empty handled_access_fs. */ 141562306a36Sopenharmony_ci ruleset_fd = 141662306a36Sopenharmony_ci landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 141762306a36Sopenharmony_ci ASSERT_LE(-1, ruleset_fd); 141862306a36Sopenharmony_ci ASSERT_EQ(ENOMSG, errno); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* Enforces policy which deny read access to all files. */ 142162306a36Sopenharmony_ci ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 142262306a36Sopenharmony_ci ruleset_fd = 142362306a36Sopenharmony_ci landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 142462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 142562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 142662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 142762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci /* Nests a policy which deny read access to all directories. */ 143062306a36Sopenharmony_ci ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 143162306a36Sopenharmony_ci ruleset_fd = 143262306a36Sopenharmony_ci landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 143362306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 143462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 143562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 143662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci /* Enforces a second time with the same ruleset. */ 143962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 144062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_on_mountpoint) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci const struct rule rules[] = { 144662306a36Sopenharmony_ci { 144762306a36Sopenharmony_ci .path = dir_s1d1, 144862306a36Sopenharmony_ci .access = ACCESS_RO, 144962306a36Sopenharmony_ci }, 145062306a36Sopenharmony_ci { 145162306a36Sopenharmony_ci /* dir_s3d2 is a mount point. */ 145262306a36Sopenharmony_ci .path = dir_s3d2, 145362306a36Sopenharmony_ci .access = ACCESS_RO, 145462306a36Sopenharmony_ci }, 145562306a36Sopenharmony_ci {}, 145662306a36Sopenharmony_ci }; 145762306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 146062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 146162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 146862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 146962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_over_mountpoint) 147362306a36Sopenharmony_ci{ 147462306a36Sopenharmony_ci const struct rule rules[] = { 147562306a36Sopenharmony_ci { 147662306a36Sopenharmony_ci .path = dir_s1d1, 147762306a36Sopenharmony_ci .access = ACCESS_RO, 147862306a36Sopenharmony_ci }, 147962306a36Sopenharmony_ci { 148062306a36Sopenharmony_ci /* dir_s3d2 is a mount point. */ 148162306a36Sopenharmony_ci .path = dir_s3d1, 148262306a36Sopenharmony_ci .access = ACCESS_RO, 148362306a36Sopenharmony_ci }, 148462306a36Sopenharmony_ci {}, 148562306a36Sopenharmony_ci }; 148662306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 148962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 149062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 149762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 149862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci/* 150262306a36Sopenharmony_ci * This test verifies that we can apply a landlock rule on the root directory 150362306a36Sopenharmony_ci * (which might require special handling). 150462306a36Sopenharmony_ci */ 150562306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_over_root_allow_then_deny) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci struct rule rules[] = { 150862306a36Sopenharmony_ci { 150962306a36Sopenharmony_ci .path = "/", 151062306a36Sopenharmony_ci .access = ACCESS_RO, 151162306a36Sopenharmony_ci }, 151262306a36Sopenharmony_ci {}, 151362306a36Sopenharmony_ci }; 151462306a36Sopenharmony_ci int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 151762306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 151862306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* Checks allowed access. */ 152162306a36Sopenharmony_ci ASSERT_EQ(0, test_open("/", O_RDONLY)); 152262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 152562306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 152662306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 152762306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 152862306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci /* Checks denied access (on a directory). */ 153162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 153262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 153362306a36Sopenharmony_ci} 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_over_root_deny) 153662306a36Sopenharmony_ci{ 153762306a36Sopenharmony_ci const struct rule rules[] = { 153862306a36Sopenharmony_ci { 153962306a36Sopenharmony_ci .path = "/", 154062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 154162306a36Sopenharmony_ci }, 154262306a36Sopenharmony_ci {}, 154362306a36Sopenharmony_ci }; 154462306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 154762306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 154862306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci /* Checks denied access (on a directory). */ 155162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 155262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 155362306a36Sopenharmony_ci} 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ciTEST_F_FORK(layout1, rule_inside_mount_ns) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci const struct rule rules[] = { 155862306a36Sopenharmony_ci { 155962306a36Sopenharmony_ci .path = "s3d3", 156062306a36Sopenharmony_ci .access = ACCESS_RO, 156162306a36Sopenharmony_ci }, 156262306a36Sopenharmony_ci {}, 156362306a36Sopenharmony_ci }; 156462306a36Sopenharmony_ci int ruleset_fd; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 156762306a36Sopenharmony_ci ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 156862306a36Sopenharmony_ci { 156962306a36Sopenharmony_ci TH_LOG("Failed to pivot root: %s", strerror(errno)); 157062306a36Sopenharmony_ci }; 157162306a36Sopenharmony_ci ASSERT_EQ(0, chdir("/")); 157262306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 157562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 157662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 157762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 158062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 158162306a36Sopenharmony_ci} 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ciTEST_F_FORK(layout1, mount_and_pivot) 158462306a36Sopenharmony_ci{ 158562306a36Sopenharmony_ci const struct rule rules[] = { 158662306a36Sopenharmony_ci { 158762306a36Sopenharmony_ci .path = dir_s3d2, 158862306a36Sopenharmony_ci .access = ACCESS_RO, 158962306a36Sopenharmony_ci }, 159062306a36Sopenharmony_ci {}, 159162306a36Sopenharmony_ci }; 159262306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 159562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 159662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 159962306a36Sopenharmony_ci ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 160062306a36Sopenharmony_ci ASSERT_EQ(EPERM, errno); 160162306a36Sopenharmony_ci ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 160262306a36Sopenharmony_ci ASSERT_EQ(EPERM, errno); 160362306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ciTEST_F_FORK(layout1, move_mount) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci const struct rule rules[] = { 160962306a36Sopenharmony_ci { 161062306a36Sopenharmony_ci .path = dir_s3d2, 161162306a36Sopenharmony_ci .access = ACCESS_RO, 161262306a36Sopenharmony_ci }, 161362306a36Sopenharmony_ci {}, 161462306a36Sopenharmony_ci }; 161562306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 162062306a36Sopenharmony_ci ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 162162306a36Sopenharmony_ci dir_s1d2, 0)) 162262306a36Sopenharmony_ci { 162362306a36Sopenharmony_ci TH_LOG("Failed to move mount: %s", strerror(errno)); 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 162762306a36Sopenharmony_ci dir_s3d2, 0)); 162862306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 163162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 163462306a36Sopenharmony_ci ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 163562306a36Sopenharmony_ci dir_s1d2, 0)); 163662306a36Sopenharmony_ci ASSERT_EQ(EPERM, errno); 163762306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ciTEST_F_FORK(layout1, release_inodes) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci const struct rule rules[] = { 164362306a36Sopenharmony_ci { 164462306a36Sopenharmony_ci .path = dir_s1d1, 164562306a36Sopenharmony_ci .access = ACCESS_RO, 164662306a36Sopenharmony_ci }, 164762306a36Sopenharmony_ci { 164862306a36Sopenharmony_ci .path = dir_s3d2, 164962306a36Sopenharmony_ci .access = ACCESS_RO, 165062306a36Sopenharmony_ci }, 165162306a36Sopenharmony_ci { 165262306a36Sopenharmony_ci .path = dir_s3d3, 165362306a36Sopenharmony_ci .access = ACCESS_RO, 165462306a36Sopenharmony_ci }, 165562306a36Sopenharmony_ci {}, 165662306a36Sopenharmony_ci }; 165762306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 166062306a36Sopenharmony_ci /* Unmount a file hierarchy while it is being used by a ruleset. */ 166162306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 166262306a36Sopenharmony_ci ASSERT_EQ(0, umount(dir_s3d2)); 166362306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 166662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 166962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 167062306a36Sopenharmony_ci /* This dir_s3d3 would not be allowed and does not exist anyway. */ 167162306a36Sopenharmony_ci ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_cienum relative_access { 167562306a36Sopenharmony_ci REL_OPEN, 167662306a36Sopenharmony_ci REL_CHDIR, 167762306a36Sopenharmony_ci REL_CHROOT_ONLY, 167862306a36Sopenharmony_ci REL_CHROOT_CHDIR, 167962306a36Sopenharmony_ci}; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic void test_relative_path(struct __test_metadata *const _metadata, 168262306a36Sopenharmony_ci const enum relative_access rel) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci /* 168562306a36Sopenharmony_ci * Common layer to check that chroot doesn't ignore it (i.e. a chroot 168662306a36Sopenharmony_ci * is not a disconnected root directory). 168762306a36Sopenharmony_ci */ 168862306a36Sopenharmony_ci const struct rule layer1_base[] = { 168962306a36Sopenharmony_ci { 169062306a36Sopenharmony_ci .path = TMP_DIR, 169162306a36Sopenharmony_ci .access = ACCESS_RO, 169262306a36Sopenharmony_ci }, 169362306a36Sopenharmony_ci {}, 169462306a36Sopenharmony_ci }; 169562306a36Sopenharmony_ci const struct rule layer2_subs[] = { 169662306a36Sopenharmony_ci { 169762306a36Sopenharmony_ci .path = dir_s1d2, 169862306a36Sopenharmony_ci .access = ACCESS_RO, 169962306a36Sopenharmony_ci }, 170062306a36Sopenharmony_ci { 170162306a36Sopenharmony_ci .path = dir_s2d2, 170262306a36Sopenharmony_ci .access = ACCESS_RO, 170362306a36Sopenharmony_ci }, 170462306a36Sopenharmony_ci {}, 170562306a36Sopenharmony_ci }; 170662306a36Sopenharmony_ci int dirfd, ruleset_fd; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 170962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 171062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 171162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 171662306a36Sopenharmony_ci switch (rel) { 171762306a36Sopenharmony_ci case REL_OPEN: 171862306a36Sopenharmony_ci case REL_CHDIR: 171962306a36Sopenharmony_ci break; 172062306a36Sopenharmony_ci case REL_CHROOT_ONLY: 172162306a36Sopenharmony_ci ASSERT_EQ(0, chdir(dir_s2d2)); 172262306a36Sopenharmony_ci break; 172362306a36Sopenharmony_ci case REL_CHROOT_CHDIR: 172462306a36Sopenharmony_ci ASSERT_EQ(0, chdir(dir_s1d2)); 172562306a36Sopenharmony_ci break; 172662306a36Sopenharmony_ci default: 172762306a36Sopenharmony_ci ASSERT_TRUE(false); 172862306a36Sopenharmony_ci return; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_CHROOT); 173262306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci switch (rel) { 173562306a36Sopenharmony_ci case REL_OPEN: 173662306a36Sopenharmony_ci dirfd = open(dir_s1d2, O_DIRECTORY); 173762306a36Sopenharmony_ci ASSERT_LE(0, dirfd); 173862306a36Sopenharmony_ci break; 173962306a36Sopenharmony_ci case REL_CHDIR: 174062306a36Sopenharmony_ci ASSERT_EQ(0, chdir(dir_s1d2)); 174162306a36Sopenharmony_ci dirfd = AT_FDCWD; 174262306a36Sopenharmony_ci break; 174362306a36Sopenharmony_ci case REL_CHROOT_ONLY: 174462306a36Sopenharmony_ci /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 174562306a36Sopenharmony_ci ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 174662306a36Sopenharmony_ci { 174762306a36Sopenharmony_ci TH_LOG("Failed to chroot: %s", strerror(errno)); 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci dirfd = AT_FDCWD; 175062306a36Sopenharmony_ci break; 175162306a36Sopenharmony_ci case REL_CHROOT_CHDIR: 175262306a36Sopenharmony_ci /* Do chroot into dir_s1d2. */ 175362306a36Sopenharmony_ci ASSERT_EQ(0, chroot(".")) 175462306a36Sopenharmony_ci { 175562306a36Sopenharmony_ci TH_LOG("Failed to chroot: %s", strerror(errno)); 175662306a36Sopenharmony_ci } 175762306a36Sopenharmony_ci dirfd = AT_FDCWD; 175862306a36Sopenharmony_ci break; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 176262306a36Sopenharmony_ci test_open_rel(dirfd, "..", O_RDONLY)); 176362306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci if (rel == REL_CHROOT_ONLY) { 176662306a36Sopenharmony_ci /* The current directory is dir_s2d2. */ 176762306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 176862306a36Sopenharmony_ci } else { 176962306a36Sopenharmony_ci /* The current directory is dir_s1d2. */ 177062306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 177462306a36Sopenharmony_ci /* Checks the root dir_s1d2. */ 177562306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 177662306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 177762306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 177862306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 177962306a36Sopenharmony_ci } 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci if (rel != REL_CHROOT_CHDIR) { 178262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 178362306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 178462306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 178562306a36Sopenharmony_ci O_RDONLY)); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 178862306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 178962306a36Sopenharmony_ci ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 179062306a36Sopenharmony_ci O_RDONLY)); 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci if (rel == REL_OPEN) 179462306a36Sopenharmony_ci ASSERT_EQ(0, close(dirfd)); 179562306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 179662306a36Sopenharmony_ci} 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_open) 179962306a36Sopenharmony_ci{ 180062306a36Sopenharmony_ci test_relative_path(_metadata, REL_OPEN); 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_chdir) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci test_relative_path(_metadata, REL_CHDIR); 180662306a36Sopenharmony_ci} 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_chroot_only) 180962306a36Sopenharmony_ci{ 181062306a36Sopenharmony_ci test_relative_path(_metadata, REL_CHROOT_ONLY); 181162306a36Sopenharmony_ci} 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ciTEST_F_FORK(layout1, relative_chroot_chdir) 181462306a36Sopenharmony_ci{ 181562306a36Sopenharmony_ci test_relative_path(_metadata, REL_CHROOT_CHDIR); 181662306a36Sopenharmony_ci} 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_cistatic void copy_binary(struct __test_metadata *const _metadata, 181962306a36Sopenharmony_ci const char *const dst_path) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci int dst_fd, src_fd; 182262306a36Sopenharmony_ci struct stat statbuf; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 182562306a36Sopenharmony_ci ASSERT_LE(0, dst_fd) 182662306a36Sopenharmony_ci { 182762306a36Sopenharmony_ci TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 183062306a36Sopenharmony_ci ASSERT_LE(0, src_fd) 183162306a36Sopenharmony_ci { 183262306a36Sopenharmony_ci TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 183362306a36Sopenharmony_ci strerror(errno)); 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci ASSERT_EQ(0, fstat(src_fd, &statbuf)); 183662306a36Sopenharmony_ci ASSERT_EQ(statbuf.st_size, 183762306a36Sopenharmony_ci sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 183862306a36Sopenharmony_ci ASSERT_EQ(0, close(src_fd)); 183962306a36Sopenharmony_ci ASSERT_EQ(0, close(dst_fd)); 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_cistatic void test_execute(struct __test_metadata *const _metadata, const int err, 184362306a36Sopenharmony_ci const char *const path) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci int status; 184662306a36Sopenharmony_ci char *const argv[] = { (char *)path, NULL }; 184762306a36Sopenharmony_ci const pid_t child = fork(); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci ASSERT_LE(0, child); 185062306a36Sopenharmony_ci if (child == 0) { 185162306a36Sopenharmony_ci ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 185262306a36Sopenharmony_ci { 185362306a36Sopenharmony_ci TH_LOG("Failed to execute \"%s\": %s", path, 185462306a36Sopenharmony_ci strerror(errno)); 185562306a36Sopenharmony_ci }; 185662306a36Sopenharmony_ci ASSERT_EQ(err, errno); 185762306a36Sopenharmony_ci _exit(_metadata->passed ? 2 : 1); 185862306a36Sopenharmony_ci return; 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci ASSERT_EQ(child, waitpid(child, &status, 0)); 186162306a36Sopenharmony_ci ASSERT_EQ(1, WIFEXITED(status)); 186262306a36Sopenharmony_ci ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 186362306a36Sopenharmony_ci { 186462306a36Sopenharmony_ci TH_LOG("Unexpected return code for \"%s\": %s", path, 186562306a36Sopenharmony_ci strerror(errno)); 186662306a36Sopenharmony_ci }; 186762306a36Sopenharmony_ci} 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ciTEST_F_FORK(layout1, execute) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci const struct rule rules[] = { 187262306a36Sopenharmony_ci { 187362306a36Sopenharmony_ci .path = dir_s1d2, 187462306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_EXECUTE, 187562306a36Sopenharmony_ci }, 187662306a36Sopenharmony_ci {}, 187762306a36Sopenharmony_ci }; 187862306a36Sopenharmony_ci const int ruleset_fd = 187962306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 188262306a36Sopenharmony_ci copy_binary(_metadata, file1_s1d1); 188362306a36Sopenharmony_ci copy_binary(_metadata, file1_s1d2); 188462306a36Sopenharmony_ci copy_binary(_metadata, file1_s1d3); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 188762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 189062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 189162306a36Sopenharmony_ci test_execute(_metadata, EACCES, file1_s1d1); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 189462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 189562306a36Sopenharmony_ci test_execute(_metadata, 0, file1_s1d2); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 189862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 189962306a36Sopenharmony_ci test_execute(_metadata, 0, file1_s1d3); 190062306a36Sopenharmony_ci} 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ciTEST_F_FORK(layout1, link) 190362306a36Sopenharmony_ci{ 190462306a36Sopenharmony_ci const struct rule layer1[] = { 190562306a36Sopenharmony_ci { 190662306a36Sopenharmony_ci .path = dir_s1d2, 190762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 190862306a36Sopenharmony_ci }, 190962306a36Sopenharmony_ci {}, 191062306a36Sopenharmony_ci }; 191162306a36Sopenharmony_ci const struct rule layer2[] = { 191262306a36Sopenharmony_ci { 191362306a36Sopenharmony_ci .path = dir_s1d3, 191462306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 191562306a36Sopenharmony_ci }, 191662306a36Sopenharmony_ci {}, 191762306a36Sopenharmony_ci }; 191862306a36Sopenharmony_ci int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 192362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 192462306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 192762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 193062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci /* Denies linking because of reparenting. */ 193362306a36Sopenharmony_ci ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 193462306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 193562306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 193662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 193762306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 193862306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 194162306a36Sopenharmony_ci ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci /* Prepares for next unlinks. */ 194462306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d2)); 194562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 194862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 194962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 195062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci /* Checks that linkind doesn't require the ability to delete a file. */ 195362306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 195462306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 195562306a36Sopenharmony_ci} 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_cistatic int test_rename(const char *const oldpath, const char *const newpath) 195862306a36Sopenharmony_ci{ 195962306a36Sopenharmony_ci if (rename(oldpath, newpath)) 196062306a36Sopenharmony_ci return errno; 196162306a36Sopenharmony_ci return 0; 196262306a36Sopenharmony_ci} 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_cistatic int test_exchange(const char *const oldpath, const char *const newpath) 196562306a36Sopenharmony_ci{ 196662306a36Sopenharmony_ci if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 196762306a36Sopenharmony_ci return errno; 196862306a36Sopenharmony_ci return 0; 196962306a36Sopenharmony_ci} 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ciTEST_F_FORK(layout1, rename_file) 197262306a36Sopenharmony_ci{ 197362306a36Sopenharmony_ci const struct rule rules[] = { 197462306a36Sopenharmony_ci { 197562306a36Sopenharmony_ci .path = dir_s1d3, 197662306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 197762306a36Sopenharmony_ci }, 197862306a36Sopenharmony_ci { 197962306a36Sopenharmony_ci .path = dir_s2d2, 198062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 198162306a36Sopenharmony_ci }, 198262306a36Sopenharmony_ci {}, 198362306a36Sopenharmony_ci }; 198462306a36Sopenharmony_ci const int ruleset_fd = 198562306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 199262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci /* 199562306a36Sopenharmony_ci * Tries to replace a file, from a directory that allows file removal, 199662306a36Sopenharmony_ci * but to a different directory (which also allows file removal). 199762306a36Sopenharmony_ci */ 199862306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 199962306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 200062306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 200162306a36Sopenharmony_ci RENAME_EXCHANGE)); 200262306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 200362306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 200462306a36Sopenharmony_ci RENAME_EXCHANGE)); 200562306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci /* 200862306a36Sopenharmony_ci * Tries to replace a file, from a directory that denies file removal, 200962306a36Sopenharmony_ci * to a different directory (which allows file removal). 201062306a36Sopenharmony_ci */ 201162306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 201262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 201362306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 201462306a36Sopenharmony_ci RENAME_EXCHANGE)); 201562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 201662306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 201762306a36Sopenharmony_ci RENAME_EXCHANGE)); 201862306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* Exchanges files and directories that partially allow removal. */ 202162306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 202262306a36Sopenharmony_ci RENAME_EXCHANGE)); 202362306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 202462306a36Sopenharmony_ci /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 202562306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 202662306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 202762306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 202862306a36Sopenharmony_ci RENAME_EXCHANGE)); 202962306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 203062306a36Sopenharmony_ci /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 203162306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 203262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci /* Renames files with different parents. */ 203562306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 203662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 203762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 203862306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 203962306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci /* Exchanges and renames files with same parent. */ 204262306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 204362306a36Sopenharmony_ci RENAME_EXCHANGE)); 204462306a36Sopenharmony_ci ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci /* Exchanges files and directories with same parent, twice. */ 204762306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 204862306a36Sopenharmony_ci RENAME_EXCHANGE)); 204962306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 205062306a36Sopenharmony_ci RENAME_EXCHANGE)); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ciTEST_F_FORK(layout1, rename_dir) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci const struct rule rules[] = { 205662306a36Sopenharmony_ci { 205762306a36Sopenharmony_ci .path = dir_s1d2, 205862306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 205962306a36Sopenharmony_ci }, 206062306a36Sopenharmony_ci { 206162306a36Sopenharmony_ci .path = dir_s2d1, 206262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 206362306a36Sopenharmony_ci }, 206462306a36Sopenharmony_ci {}, 206562306a36Sopenharmony_ci }; 206662306a36Sopenharmony_ci const int ruleset_fd = 206762306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci /* Empties dir_s1d3 to allow renaming. */ 207262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 207362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 207662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci /* Exchanges and renames directory to a different parent. */ 207962306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 208062306a36Sopenharmony_ci RENAME_EXCHANGE)); 208162306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 208262306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 208362306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 208462306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 208562306a36Sopenharmony_ci RENAME_EXCHANGE)); 208662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci /* 208962306a36Sopenharmony_ci * Exchanges directory to the same parent, which doesn't allow 209062306a36Sopenharmony_ci * directory removal. 209162306a36Sopenharmony_ci */ 209262306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 209362306a36Sopenharmony_ci RENAME_EXCHANGE)); 209462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 209562306a36Sopenharmony_ci /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 209662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 209762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 209862306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 209962306a36Sopenharmony_ci RENAME_EXCHANGE)); 210062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 210162306a36Sopenharmony_ci /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 210262306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 210362306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci /* 210662306a36Sopenharmony_ci * Exchanges and renames directory to the same parent, which allows 210762306a36Sopenharmony_ci * directory removal. 210862306a36Sopenharmony_ci */ 210962306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 211062306a36Sopenharmony_ci RENAME_EXCHANGE)); 211162306a36Sopenharmony_ci ASSERT_EQ(0, unlink(dir_s1d3)); 211262306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 211362306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 211462306a36Sopenharmony_ci ASSERT_EQ(0, rmdir(dir_s1d3)); 211562306a36Sopenharmony_ci} 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_refer) 211862306a36Sopenharmony_ci{ 211962306a36Sopenharmony_ci const struct rule layer1[] = { 212062306a36Sopenharmony_ci { 212162306a36Sopenharmony_ci .path = dir_s1d2, 212262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 212362306a36Sopenharmony_ci }, 212462306a36Sopenharmony_ci { 212562306a36Sopenharmony_ci .path = dir_s2d2, 212662306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 212762306a36Sopenharmony_ci }, 212862306a36Sopenharmony_ci {}, 212962306a36Sopenharmony_ci }; 213062306a36Sopenharmony_ci int ruleset_fd = 213162306a36Sopenharmony_ci create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 213462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 213562306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 213862306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 213962306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 214062306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 214162306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 214262306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 214562306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 214662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 214762306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 214862306a36Sopenharmony_ci /* 214962306a36Sopenharmony_ci * Moving should only be allowed when the source and the destination 215062306a36Sopenharmony_ci * parent directory have REFER. 215162306a36Sopenharmony_ci */ 215262306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 215362306a36Sopenharmony_ci ASSERT_EQ(ENOTEMPTY, errno); 215462306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s2d3)); 215562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s2d3)); 215662306a36Sopenharmony_ci ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 215762306a36Sopenharmony_ci} 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci/* Checks renames beneath dir_s1d1. */ 216062306a36Sopenharmony_cistatic void refer_denied_by_default(struct __test_metadata *const _metadata, 216162306a36Sopenharmony_ci const struct rule layer1[], 216262306a36Sopenharmony_ci const int layer1_err, 216362306a36Sopenharmony_ci const struct rule layer2[]) 216462306a36Sopenharmony_ci{ 216562306a36Sopenharmony_ci int ruleset_fd; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 217062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 217162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 217262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci /* 217562306a36Sopenharmony_ci * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 217662306a36Sopenharmony_ci * layer1_err), then it allows some different-parent renames and links. 217762306a36Sopenharmony_ci */ 217862306a36Sopenharmony_ci ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 217962306a36Sopenharmony_ci if (layer1_err == 0) 218062306a36Sopenharmony_ci ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 218162306a36Sopenharmony_ci ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 218262306a36Sopenharmony_ci ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 218562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 218662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 218762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci /* 219062306a36Sopenharmony_ci * Now, either the first or the second layer does not handle 219162306a36Sopenharmony_ci * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 219262306a36Sopenharmony_ci * renames and links are denied, thus making the layer handling 219362306a36Sopenharmony_ci * LANDLOCK_ACCESS_FS_REFER null and void. 219462306a36Sopenharmony_ci */ 219562306a36Sopenharmony_ci ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 219662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 219762306a36Sopenharmony_ci ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 219862306a36Sopenharmony_ci} 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ciconst struct rule layer_dir_s1d1_refer[] = { 220162306a36Sopenharmony_ci { 220262306a36Sopenharmony_ci .path = dir_s1d1, 220362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 220462306a36Sopenharmony_ci }, 220562306a36Sopenharmony_ci {}, 220662306a36Sopenharmony_ci}; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ciconst struct rule layer_dir_s1d1_execute[] = { 220962306a36Sopenharmony_ci { 221062306a36Sopenharmony_ci /* Matches a parent directory. */ 221162306a36Sopenharmony_ci .path = dir_s1d1, 221262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_EXECUTE, 221362306a36Sopenharmony_ci }, 221462306a36Sopenharmony_ci {}, 221562306a36Sopenharmony_ci}; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ciconst struct rule layer_dir_s2d1_execute[] = { 221862306a36Sopenharmony_ci { 221962306a36Sopenharmony_ci /* Does not match a parent directory. */ 222062306a36Sopenharmony_ci .path = dir_s2d1, 222162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_EXECUTE, 222262306a36Sopenharmony_ci }, 222362306a36Sopenharmony_ci {}, 222462306a36Sopenharmony_ci}; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci/* 222762306a36Sopenharmony_ci * Tests precedence over renames: denied by default for different parent 222862306a36Sopenharmony_ci * directories, *with* a rule matching a parent directory, but not directly 222962306a36Sopenharmony_ci * denying access (with MAKE_REG nor REMOVE). 223062306a36Sopenharmony_ci */ 223162306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default1) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 223462306a36Sopenharmony_ci layer_dir_s1d1_execute); 223562306a36Sopenharmony_ci} 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci/* 223862306a36Sopenharmony_ci * Same test but this time turning around the ABI version order: the first 223962306a36Sopenharmony_ci * layer does not handle LANDLOCK_ACCESS_FS_REFER. 224062306a36Sopenharmony_ci */ 224162306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default2) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 224462306a36Sopenharmony_ci layer_dir_s1d1_refer); 224562306a36Sopenharmony_ci} 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci/* 224862306a36Sopenharmony_ci * Tests precedence over renames: denied by default for different parent 224962306a36Sopenharmony_ci * directories, *without* a rule matching a parent directory, but not directly 225062306a36Sopenharmony_ci * denying access (with MAKE_REG nor REMOVE). 225162306a36Sopenharmony_ci */ 225262306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default3) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 225562306a36Sopenharmony_ci layer_dir_s2d1_execute); 225662306a36Sopenharmony_ci} 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci/* 225962306a36Sopenharmony_ci * Same test but this time turning around the ABI version order: the first 226062306a36Sopenharmony_ci * layer does not handle LANDLOCK_ACCESS_FS_REFER. 226162306a36Sopenharmony_ci */ 226262306a36Sopenharmony_ciTEST_F_FORK(layout1, refer_denied_by_default4) 226362306a36Sopenharmony_ci{ 226462306a36Sopenharmony_ci refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 226562306a36Sopenharmony_ci layer_dir_s1d1_refer); 226662306a36Sopenharmony_ci} 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_link) 226962306a36Sopenharmony_ci{ 227062306a36Sopenharmony_ci const struct rule layer1[] = { 227162306a36Sopenharmony_ci { 227262306a36Sopenharmony_ci .path = dir_s1d2, 227362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 227462306a36Sopenharmony_ci }, 227562306a36Sopenharmony_ci { 227662306a36Sopenharmony_ci .path = dir_s1d3, 227762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 227862306a36Sopenharmony_ci }, 227962306a36Sopenharmony_ci { 228062306a36Sopenharmony_ci .path = dir_s2d2, 228162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 228262306a36Sopenharmony_ci }, 228362306a36Sopenharmony_ci { 228462306a36Sopenharmony_ci .path = dir_s2d3, 228562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 228662306a36Sopenharmony_ci }, 228762306a36Sopenharmony_ci {}, 228862306a36Sopenharmony_ci }; 228962306a36Sopenharmony_ci const int ruleset_fd = create_ruleset( 229062306a36Sopenharmony_ci _metadata, 229162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 229462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 229562306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 229862306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 229962306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* Denies linking because of missing MAKE_REG. */ 230262306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 230362306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 230462306a36Sopenharmony_ci /* Denies linking because of missing source and destination REFER. */ 230562306a36Sopenharmony_ci ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 230662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 230762306a36Sopenharmony_ci /* Denies linking because of missing source REFER. */ 230862306a36Sopenharmony_ci ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 230962306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci /* Denies linking because of missing MAKE_REG. */ 231262306a36Sopenharmony_ci ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 231362306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 231462306a36Sopenharmony_ci /* Denies linking because of missing destination REFER. */ 231562306a36Sopenharmony_ci ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 231662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci /* Allows linking because of REFER and MAKE_REG. */ 231962306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 232062306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s2d2)); 232162306a36Sopenharmony_ci /* Reverse linking denied because of missing MAKE_REG. */ 232262306a36Sopenharmony_ci ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 232362306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 232462306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s2d3)); 232562306a36Sopenharmony_ci /* Checks reverse linking. */ 232662306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 232762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci /* 233062306a36Sopenharmony_ci * This is OK for a file link, but it should not be allowed for a 233162306a36Sopenharmony_ci * directory rename (because of the superset of access rights. 233262306a36Sopenharmony_ci */ 233362306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 233462306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 233762306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 233862306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 233962306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 234262306a36Sopenharmony_ci ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 234362306a36Sopenharmony_ci} 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_rename) 234662306a36Sopenharmony_ci{ 234762306a36Sopenharmony_ci /* Same rules as for reparent_link. */ 234862306a36Sopenharmony_ci const struct rule layer1[] = { 234962306a36Sopenharmony_ci { 235062306a36Sopenharmony_ci .path = dir_s1d2, 235162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 235262306a36Sopenharmony_ci }, 235362306a36Sopenharmony_ci { 235462306a36Sopenharmony_ci .path = dir_s1d3, 235562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 235662306a36Sopenharmony_ci }, 235762306a36Sopenharmony_ci { 235862306a36Sopenharmony_ci .path = dir_s2d2, 235962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 236062306a36Sopenharmony_ci }, 236162306a36Sopenharmony_ci { 236262306a36Sopenharmony_ci .path = dir_s2d3, 236362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 236462306a36Sopenharmony_ci }, 236562306a36Sopenharmony_ci {}, 236662306a36Sopenharmony_ci }; 236762306a36Sopenharmony_ci const int ruleset_fd = create_ruleset( 236862306a36Sopenharmony_ci _metadata, 236962306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 237262306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 237362306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 237662306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* Denies renaming because of missing MAKE_REG. */ 237962306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 238062306a36Sopenharmony_ci RENAME_EXCHANGE)); 238162306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 238262306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 238362306a36Sopenharmony_ci RENAME_EXCHANGE)); 238462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 238562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 238662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 238762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 238862306a36Sopenharmony_ci /* Even denies same file exchange. */ 238962306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 239062306a36Sopenharmony_ci RENAME_EXCHANGE)); 239162306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci /* Denies renaming because of missing source and destination REFER. */ 239462306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 239562306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 239662306a36Sopenharmony_ci /* 239762306a36Sopenharmony_ci * Denies renaming because of missing MAKE_REG, source and destination 239862306a36Sopenharmony_ci * REFER. 239962306a36Sopenharmony_ci */ 240062306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 240162306a36Sopenharmony_ci RENAME_EXCHANGE)); 240262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 240362306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 240462306a36Sopenharmony_ci RENAME_EXCHANGE)); 240562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci /* Denies renaming because of missing source REFER. */ 240862306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 240962306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 241062306a36Sopenharmony_ci /* Denies renaming because of missing MAKE_REG. */ 241162306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 241262306a36Sopenharmony_ci RENAME_EXCHANGE)); 241362306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci /* Denies renaming because of missing MAKE_REG. */ 241662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 241762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 241862306a36Sopenharmony_ci /* Denies renaming because of missing destination REFER*/ 241962306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 242062306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci /* Denies exchange because of one missing MAKE_REG. */ 242362306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 242462306a36Sopenharmony_ci RENAME_EXCHANGE)); 242562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 242662306a36Sopenharmony_ci /* Allows renaming because of REFER and MAKE_REG. */ 242762306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci /* Reverse renaming denied because of missing MAKE_REG. */ 243062306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 243162306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 243262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s2d3)); 243362306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci /* Tests reverse renaming. */ 243662306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 243762306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 243862306a36Sopenharmony_ci RENAME_EXCHANGE)); 243962306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci /* 244262306a36Sopenharmony_ci * This is OK for a file rename, but it should not be allowed for a 244362306a36Sopenharmony_ci * directory rename (because of the superset of access rights). 244462306a36Sopenharmony_ci */ 244562306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 244662306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci /* 244962306a36Sopenharmony_ci * Tests superset restrictions applied to directories. Not only the 245062306a36Sopenharmony_ci * dir_s2d3's parent (dir_s2d2) should be taken into account but also 245162306a36Sopenharmony_ci * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 245262306a36Sopenharmony_ci * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 245362306a36Sopenharmony_ci * directly by the moved dir_s2d3. 245462306a36Sopenharmony_ci */ 245562306a36Sopenharmony_ci ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 245662306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 245762306a36Sopenharmony_ci /* 245862306a36Sopenharmony_ci * The first rename is allowed but not the exchange because dir_s1d3's 245962306a36Sopenharmony_ci * parent (dir_s1d2) doesn't have REFER. 246062306a36Sopenharmony_ci */ 246162306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 246262306a36Sopenharmony_ci RENAME_EXCHANGE)); 246362306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 246462306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 246562306a36Sopenharmony_ci RENAME_EXCHANGE)); 246662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 246762306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 246862306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 247162306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 247262306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 247362306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci /* Renaming in the same directory is always allowed. */ 247662306a36Sopenharmony_ci ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 247762306a36Sopenharmony_ci ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 248062306a36Sopenharmony_ci /* Denies because of missing source MAKE_REG and destination REFER. */ 248162306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 248262306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 248562306a36Sopenharmony_ci /* Denies because of missing source MAKE_REG and REFER. */ 248662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 248762306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 248862306a36Sopenharmony_ci} 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_cistatic void 249162306a36Sopenharmony_cireparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 249262306a36Sopenharmony_ci{ 249362306a36Sopenharmony_ci const struct rule layer1[] = { 249462306a36Sopenharmony_ci { 249562306a36Sopenharmony_ci .path = dir_s1d2, 249662306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 249762306a36Sopenharmony_ci }, 249862306a36Sopenharmony_ci { 249962306a36Sopenharmony_ci /* Interesting for the layer2 tests. */ 250062306a36Sopenharmony_ci .path = dir_s1d3, 250162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 250262306a36Sopenharmony_ci }, 250362306a36Sopenharmony_ci { 250462306a36Sopenharmony_ci .path = dir_s2d2, 250562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 250662306a36Sopenharmony_ci }, 250762306a36Sopenharmony_ci { 250862306a36Sopenharmony_ci .path = dir_s2d3, 250962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_REG, 251062306a36Sopenharmony_ci }, 251162306a36Sopenharmony_ci {}, 251262306a36Sopenharmony_ci }; 251362306a36Sopenharmony_ci const int ruleset_fd = create_ruleset( 251462306a36Sopenharmony_ci _metadata, 251562306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 251862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 251962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 252062306a36Sopenharmony_ci} 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_cistatic void 252362306a36Sopenharmony_cireparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 252462306a36Sopenharmony_ci{ 252562306a36Sopenharmony_ci const struct rule layer2[] = { 252662306a36Sopenharmony_ci { 252762306a36Sopenharmony_ci .path = dir_s2d3, 252862306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 252962306a36Sopenharmony_ci }, 253062306a36Sopenharmony_ci {}, 253162306a36Sopenharmony_ci }; 253262306a36Sopenharmony_ci /* 253362306a36Sopenharmony_ci * Same checks as before but with a second layer and a new MAKE_DIR 253462306a36Sopenharmony_ci * rule (and no explicit handling of REFER). 253562306a36Sopenharmony_ci */ 253662306a36Sopenharmony_ci const int ruleset_fd = 253762306a36Sopenharmony_ci create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 254062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 254162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_rename1) 254562306a36Sopenharmony_ci{ 254662306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s2d2)); 254762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s2d3)); 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci reparent_exdev_layers_enforce1(_metadata); 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci /* 255262306a36Sopenharmony_ci * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 255362306a36Sopenharmony_ci * because it doesn't inherit new access rights. 255462306a36Sopenharmony_ci */ 255562306a36Sopenharmony_ci ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 255662306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci /* 255962306a36Sopenharmony_ci * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 256062306a36Sopenharmony_ci * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 256162306a36Sopenharmony_ci * already allowed for dir_s1d3. 256262306a36Sopenharmony_ci */ 256362306a36Sopenharmony_ci ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 256462306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci /* 256762306a36Sopenharmony_ci * However, moving the file1_s1d3 file below dir_s2d3 is allowed 256862306a36Sopenharmony_ci * because it cannot inherit MAKE_REG right (which is dedicated to 256962306a36Sopenharmony_ci * directories). 257062306a36Sopenharmony_ci */ 257162306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci reparent_exdev_layers_enforce2(_metadata); 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci /* 257662306a36Sopenharmony_ci * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 257762306a36Sopenharmony_ci * MAKE_DIR is not tied to dir_s2d2. 257862306a36Sopenharmony_ci */ 257962306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 258062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci /* 258362306a36Sopenharmony_ci * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 258462306a36Sopenharmony_ci * would grants MAKE_REG and MAKE_DIR rights to it. 258562306a36Sopenharmony_ci */ 258662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 258762306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci /* 259062306a36Sopenharmony_ci * Moving the file2_s1d3 file below dir_s2d3 is denied because the 259162306a36Sopenharmony_ci * second layer does not handle REFER, which is always denied by 259262306a36Sopenharmony_ci * default. 259362306a36Sopenharmony_ci */ 259462306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 259562306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 259662306a36Sopenharmony_ci} 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_rename2) 259962306a36Sopenharmony_ci{ 260062306a36Sopenharmony_ci reparent_exdev_layers_enforce1(_metadata); 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci /* Checks EACCES predominance over EXDEV. */ 260362306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 260462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 260562306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 260662306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 260762306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 260862306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 260962306a36Sopenharmony_ci /* Modify layout! */ 261062306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci /* Without REFER source. */ 261362306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 261462306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 261562306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 261662306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci reparent_exdev_layers_enforce2(_metadata); 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci /* Checks EACCES predominance over EXDEV. */ 262162306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 262262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 262362306a36Sopenharmony_ci /* Checks with actual file2_s1d2. */ 262462306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 262562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 262662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 262762306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 262862306a36Sopenharmony_ci /* 262962306a36Sopenharmony_ci * Modifying the layout is now denied because the second layer does not 263062306a36Sopenharmony_ci * handle REFER, which is always denied by default. 263162306a36Sopenharmony_ci */ 263262306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 263362306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci /* Without REFER source, EACCES wins over EXDEV. */ 263662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 263762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 263862306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 263962306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 264062306a36Sopenharmony_ci} 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 264362306a36Sopenharmony_ci{ 264462306a36Sopenharmony_ci const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 264562306a36Sopenharmony_ci file2_s2d3; 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 264862306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 264962306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s2d3)); 265062306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci reparent_exdev_layers_enforce1(_metadata); 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci /* Error predominance with file exchange: returns EXDEV and EACCES. */ 265562306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 265662306a36Sopenharmony_ci RENAME_EXCHANGE)); 265762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 265862306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 265962306a36Sopenharmony_ci RENAME_EXCHANGE)); 266062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci /* 266362306a36Sopenharmony_ci * Checks with directories which creation could be allowed, but denied 266462306a36Sopenharmony_ci * because of access rights that would be inherited. 266562306a36Sopenharmony_ci */ 266662306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 266762306a36Sopenharmony_ci dir_file2_s2d3, RENAME_EXCHANGE)); 266862306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 266962306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 267062306a36Sopenharmony_ci dir_file1_s1d2, RENAME_EXCHANGE)); 267162306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_ci /* Checks with same access rights. */ 267462306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 267562306a36Sopenharmony_ci RENAME_EXCHANGE)); 267662306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 267762306a36Sopenharmony_ci RENAME_EXCHANGE)); 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci /* Checks with different (child-only) access rights. */ 268062306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 268162306a36Sopenharmony_ci RENAME_EXCHANGE)); 268262306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 268362306a36Sopenharmony_ci RENAME_EXCHANGE)); 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci /* 268662306a36Sopenharmony_ci * Checks that exchange between file and directory are consistent. 268762306a36Sopenharmony_ci * 268862306a36Sopenharmony_ci * Moving a file (file1_s2d2) to a directory which only grants more 268962306a36Sopenharmony_ci * directory-related access rights is allowed, and at the same time 269062306a36Sopenharmony_ci * moving a directory (dir_file2_s2d3) to another directory which 269162306a36Sopenharmony_ci * grants less access rights is allowed too. 269262306a36Sopenharmony_ci * 269362306a36Sopenharmony_ci * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 269462306a36Sopenharmony_ci */ 269562306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 269662306a36Sopenharmony_ci RENAME_EXCHANGE)); 269762306a36Sopenharmony_ci /* 269862306a36Sopenharmony_ci * However, moving back the directory is denied because it would get 269962306a36Sopenharmony_ci * more access rights than the current state and because file creation 270062306a36Sopenharmony_ci * is forbidden (in dir_s2d2). 270162306a36Sopenharmony_ci */ 270262306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 270362306a36Sopenharmony_ci RENAME_EXCHANGE)); 270462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 270562306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 270662306a36Sopenharmony_ci RENAME_EXCHANGE)); 270762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci reparent_exdev_layers_enforce2(_metadata); 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci /* Error predominance with file exchange: returns EXDEV and EACCES. */ 271262306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 271362306a36Sopenharmony_ci RENAME_EXCHANGE)); 271462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 271562306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 271662306a36Sopenharmony_ci RENAME_EXCHANGE)); 271762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci /* Checks with directories which creation is now denied. */ 272062306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 272162306a36Sopenharmony_ci dir_file2_s2d3, RENAME_EXCHANGE)); 272262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 272362306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 272462306a36Sopenharmony_ci dir_file1_s1d2, RENAME_EXCHANGE)); 272562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci /* Checks with different (child-only) access rights. */ 272862306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 272962306a36Sopenharmony_ci RENAME_EXCHANGE)); 273062306a36Sopenharmony_ci /* Denied because of MAKE_DIR. */ 273162306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 273262306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 273362306a36Sopenharmony_ci RENAME_EXCHANGE)); 273462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci /* Checks with different (child-only) access rights. */ 273762306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 273862306a36Sopenharmony_ci RENAME_EXCHANGE)); 273962306a36Sopenharmony_ci /* Denied because of MAKE_DIR. */ 274062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 274162306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 274262306a36Sopenharmony_ci RENAME_EXCHANGE)); 274362306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 274662306a36Sopenharmony_ci} 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 274962306a36Sopenharmony_ci{ 275062306a36Sopenharmony_ci const char *const dir_file2_s2d3 = file2_s2d3; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s2d3)); 275362306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci reparent_exdev_layers_enforce1(_metadata); 275662306a36Sopenharmony_ci reparent_exdev_layers_enforce2(_metadata); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci /* Checks that exchange between file and directory are consistent. */ 275962306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 276062306a36Sopenharmony_ci RENAME_EXCHANGE)); 276162306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 276262306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 276362306a36Sopenharmony_ci RENAME_EXCHANGE)); 276462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 276562306a36Sopenharmony_ci} 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 276862306a36Sopenharmony_ci{ 276962306a36Sopenharmony_ci const char *const dir_file2_s2d3 = file2_s2d3; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s2d3)); 277262306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci reparent_exdev_layers_enforce1(_metadata); 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci /* 277762306a36Sopenharmony_ci * Checks that exchange between file and directory are consistent, 277862306a36Sopenharmony_ci * including with inverted arguments (see 277962306a36Sopenharmony_ci * layout1.reparent_exdev_layers_exchange1). 278062306a36Sopenharmony_ci */ 278162306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 278262306a36Sopenharmony_ci RENAME_EXCHANGE)); 278362306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 278462306a36Sopenharmony_ci RENAME_EXCHANGE)); 278562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 278662306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 278762306a36Sopenharmony_ci RENAME_EXCHANGE)); 278862306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 278962306a36Sopenharmony_ci} 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_remove) 279262306a36Sopenharmony_ci{ 279362306a36Sopenharmony_ci const struct rule layer1[] = { 279462306a36Sopenharmony_ci { 279562306a36Sopenharmony_ci .path = dir_s1d1, 279662306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER | 279762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REMOVE_DIR, 279862306a36Sopenharmony_ci }, 279962306a36Sopenharmony_ci { 280062306a36Sopenharmony_ci .path = dir_s1d2, 280162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 280262306a36Sopenharmony_ci }, 280362306a36Sopenharmony_ci { 280462306a36Sopenharmony_ci .path = dir_s2d1, 280562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER | 280662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REMOVE_FILE, 280762306a36Sopenharmony_ci }, 280862306a36Sopenharmony_ci {}, 280962306a36Sopenharmony_ci }; 281062306a36Sopenharmony_ci const int ruleset_fd = create_ruleset( 281162306a36Sopenharmony_ci _metadata, 281262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 281362306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REMOVE_FILE, 281462306a36Sopenharmony_ci layer1); 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 281762306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 281862306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci /* Access denied because of wrong/swapped remove file/dir. */ 282162306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 282262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 282362306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 282462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 282562306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 282662306a36Sopenharmony_ci RENAME_EXCHANGE)); 282762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 282862306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 282962306a36Sopenharmony_ci RENAME_EXCHANGE)); 283062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci /* Access allowed thanks to the matching rights. */ 283362306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 283462306a36Sopenharmony_ci ASSERT_EQ(EISDIR, errno); 283562306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 283662306a36Sopenharmony_ci ASSERT_EQ(ENOTDIR, errno); 283762306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 283862306a36Sopenharmony_ci ASSERT_EQ(ENOTDIR, errno); 283962306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s2d1)); 284062306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 284162306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 284262306a36Sopenharmony_ci ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci /* Effectively removes a file and a directory by exchanging them. */ 284562306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 284662306a36Sopenharmony_ci ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 284762306a36Sopenharmony_ci RENAME_EXCHANGE)); 284862306a36Sopenharmony_ci ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 284962306a36Sopenharmony_ci RENAME_EXCHANGE)); 285062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 285162306a36Sopenharmony_ci} 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ciTEST_F_FORK(layout1, reparent_dom_superset) 285462306a36Sopenharmony_ci{ 285562306a36Sopenharmony_ci const struct rule layer1[] = { 285662306a36Sopenharmony_ci { 285762306a36Sopenharmony_ci .path = dir_s1d2, 285862306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 285962306a36Sopenharmony_ci }, 286062306a36Sopenharmony_ci { 286162306a36Sopenharmony_ci .path = file1_s1d2, 286262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_EXECUTE, 286362306a36Sopenharmony_ci }, 286462306a36Sopenharmony_ci { 286562306a36Sopenharmony_ci .path = dir_s1d3, 286662306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 286762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_EXECUTE, 286862306a36Sopenharmony_ci }, 286962306a36Sopenharmony_ci { 287062306a36Sopenharmony_ci .path = dir_s2d2, 287162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER | 287262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_EXECUTE | 287362306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_SOCK, 287462306a36Sopenharmony_ci }, 287562306a36Sopenharmony_ci { 287662306a36Sopenharmony_ci .path = dir_s2d3, 287762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 287862306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_FIFO, 287962306a36Sopenharmony_ci }, 288062306a36Sopenharmony_ci {}, 288162306a36Sopenharmony_ci }; 288262306a36Sopenharmony_ci int ruleset_fd = create_ruleset(_metadata, 288362306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REFER | 288462306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_EXECUTE | 288562306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_SOCK | 288662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | 288762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_MAKE_FIFO, 288862306a36Sopenharmony_ci layer1); 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 289162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 289262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 289562306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 289662306a36Sopenharmony_ci /* 289762306a36Sopenharmony_ci * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 289862306a36Sopenharmony_ci * access right. 289962306a36Sopenharmony_ci */ 290062306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 290162306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 290262306a36Sopenharmony_ci /* 290362306a36Sopenharmony_ci * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 290462306a36Sopenharmony_ci * superset of access rights compared to dir_s1d2, because file1_s1d2 290562306a36Sopenharmony_ci * already has these access rights anyway. 290662306a36Sopenharmony_ci */ 290762306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 290862306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 291162306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 291262306a36Sopenharmony_ci /* 291362306a36Sopenharmony_ci * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 291462306a36Sopenharmony_ci * right. 291562306a36Sopenharmony_ci */ 291662306a36Sopenharmony_ci ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 291762306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 291862306a36Sopenharmony_ci /* 291962306a36Sopenharmony_ci * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 292062306a36Sopenharmony_ci * of access rights compared to dir_s1d2, because dir_s1d3 already has 292162306a36Sopenharmony_ci * these access rights anyway. 292262306a36Sopenharmony_ci */ 292362306a36Sopenharmony_ci ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 292462306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci /* 292762306a36Sopenharmony_ci * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 292862306a36Sopenharmony_ci * will be denied because the new inherited access rights from dir_s1d2 292962306a36Sopenharmony_ci * will be less than the destination (original) dir_s2d3. This is a 293062306a36Sopenharmony_ci * sinkhole scenario where we cannot move back files or directories. 293162306a36Sopenharmony_ci */ 293262306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 293362306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 293462306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 293562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d2)); 293662306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s2d3)); 293762306a36Sopenharmony_ci /* 293862306a36Sopenharmony_ci * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 293962306a36Sopenharmony_ci * MAKE_SOCK which were inherited from dir_s1d3. 294062306a36Sopenharmony_ci */ 294162306a36Sopenharmony_ci ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 294262306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 294362306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 294462306a36Sopenharmony_ci} 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ciTEST_F_FORK(layout1, remove_dir) 294762306a36Sopenharmony_ci{ 294862306a36Sopenharmony_ci const struct rule rules[] = { 294962306a36Sopenharmony_ci { 295062306a36Sopenharmony_ci .path = dir_s1d2, 295162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 295262306a36Sopenharmony_ci }, 295362306a36Sopenharmony_ci {}, 295462306a36Sopenharmony_ci }; 295562306a36Sopenharmony_ci const int ruleset_fd = 295662306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 296162306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 296262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 296362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 296662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci ASSERT_EQ(0, rmdir(dir_s1d3)); 296962306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 297062306a36Sopenharmony_ci ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci /* dir_s1d2 itself cannot be removed. */ 297362306a36Sopenharmony_ci ASSERT_EQ(-1, rmdir(dir_s1d2)); 297462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 297562306a36Sopenharmony_ci ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 297662306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 297762306a36Sopenharmony_ci ASSERT_EQ(-1, rmdir(dir_s1d1)); 297862306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 297962306a36Sopenharmony_ci ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 298062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 298162306a36Sopenharmony_ci} 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ciTEST_F_FORK(layout1, remove_file) 298462306a36Sopenharmony_ci{ 298562306a36Sopenharmony_ci const struct rule rules[] = { 298662306a36Sopenharmony_ci { 298762306a36Sopenharmony_ci .path = dir_s1d2, 298862306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 298962306a36Sopenharmony_ci }, 299062306a36Sopenharmony_ci {}, 299162306a36Sopenharmony_ci }; 299262306a36Sopenharmony_ci const int ruleset_fd = 299362306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 299662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 299762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci ASSERT_EQ(-1, unlink(file1_s1d1)); 300062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 300162306a36Sopenharmony_ci ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 300262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 300362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 300462306a36Sopenharmony_ci ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 300562306a36Sopenharmony_ci} 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_cistatic void test_make_file(struct __test_metadata *const _metadata, 300862306a36Sopenharmony_ci const __u64 access, const mode_t mode, 300962306a36Sopenharmony_ci const dev_t dev) 301062306a36Sopenharmony_ci{ 301162306a36Sopenharmony_ci const struct rule rules[] = { 301262306a36Sopenharmony_ci { 301362306a36Sopenharmony_ci .path = dir_s1d2, 301462306a36Sopenharmony_ci .access = access, 301562306a36Sopenharmony_ci }, 301662306a36Sopenharmony_ci {}, 301762306a36Sopenharmony_ci }; 301862306a36Sopenharmony_ci const int ruleset_fd = create_ruleset(_metadata, access, rules); 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 302362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d1)); 302462306a36Sopenharmony_ci ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 302562306a36Sopenharmony_ci { 302662306a36Sopenharmony_ci TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 302762306a36Sopenharmony_ci strerror(errno)); 302862306a36Sopenharmony_ci }; 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 303162306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d2)); 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 303462306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 303762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 304062306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 304162306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 304262306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 304362306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 304462306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 304762306a36Sopenharmony_ci { 304862306a36Sopenharmony_ci TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 304962306a36Sopenharmony_ci strerror(errno)); 305062306a36Sopenharmony_ci }; 305162306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 305262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d2)); 305362306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 305662306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 305762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 305862306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 305962306a36Sopenharmony_ci} 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ciTEST_F_FORK(layout1, make_char) 306262306a36Sopenharmony_ci{ 306362306a36Sopenharmony_ci /* Creates a /dev/null device. */ 306462306a36Sopenharmony_ci set_cap(_metadata, CAP_MKNOD); 306562306a36Sopenharmony_ci test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 306662306a36Sopenharmony_ci makedev(1, 3)); 306762306a36Sopenharmony_ci} 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ciTEST_F_FORK(layout1, make_block) 307062306a36Sopenharmony_ci{ 307162306a36Sopenharmony_ci /* Creates a /dev/loop0 device. */ 307262306a36Sopenharmony_ci set_cap(_metadata, CAP_MKNOD); 307362306a36Sopenharmony_ci test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 307462306a36Sopenharmony_ci makedev(7, 0)); 307562306a36Sopenharmony_ci} 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ciTEST_F_FORK(layout1, make_reg_1) 307862306a36Sopenharmony_ci{ 307962306a36Sopenharmony_ci test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 308062306a36Sopenharmony_ci} 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ciTEST_F_FORK(layout1, make_reg_2) 308362306a36Sopenharmony_ci{ 308462306a36Sopenharmony_ci test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 308562306a36Sopenharmony_ci} 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_ciTEST_F_FORK(layout1, make_sock) 308862306a36Sopenharmony_ci{ 308962306a36Sopenharmony_ci test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 309062306a36Sopenharmony_ci} 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ciTEST_F_FORK(layout1, make_fifo) 309362306a36Sopenharmony_ci{ 309462306a36Sopenharmony_ci test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 309562306a36Sopenharmony_ci} 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ciTEST_F_FORK(layout1, make_sym) 309862306a36Sopenharmony_ci{ 309962306a36Sopenharmony_ci const struct rule rules[] = { 310062306a36Sopenharmony_ci { 310162306a36Sopenharmony_ci .path = dir_s1d2, 310262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 310362306a36Sopenharmony_ci }, 310462306a36Sopenharmony_ci {}, 310562306a36Sopenharmony_ci }; 310662306a36Sopenharmony_ci const int ruleset_fd = 310762306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 311262306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d1)); 311362306a36Sopenharmony_ci ASSERT_EQ(0, symlink("none", file2_s1d1)); 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 311662306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d2)); 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 311962306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 312262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci ASSERT_EQ(-1, symlink("none", file1_s1d1)); 312562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 312662306a36Sopenharmony_ci ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 312762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 312862306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 312962306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci ASSERT_EQ(0, symlink("none", file1_s1d2)); 313262306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 313362306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d2)); 313462306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci ASSERT_EQ(0, symlink("none", file1_s1d3)); 313762306a36Sopenharmony_ci ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 313862306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file2_s1d3)); 313962306a36Sopenharmony_ci ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 314062306a36Sopenharmony_ci} 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ciTEST_F_FORK(layout1, make_dir) 314362306a36Sopenharmony_ci{ 314462306a36Sopenharmony_ci const struct rule rules[] = { 314562306a36Sopenharmony_ci { 314662306a36Sopenharmony_ci .path = dir_s1d2, 314762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 314862306a36Sopenharmony_ci }, 314962306a36Sopenharmony_ci {}, 315062306a36Sopenharmony_ci }; 315162306a36Sopenharmony_ci const int ruleset_fd = 315262306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d1)); 315762306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 315862306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d3)); 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 316162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci /* Uses file_* as directory names. */ 316462306a36Sopenharmony_ci ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 316562306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 316662306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 316762306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 316862306a36Sopenharmony_ci} 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_cistatic int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 317162306a36Sopenharmony_ci const int open_flags) 317262306a36Sopenharmony_ci{ 317362306a36Sopenharmony_ci static const char path_template[] = "/proc/self/fd/%d"; 317462306a36Sopenharmony_ci char procfd_path[sizeof(path_template) + 10]; 317562306a36Sopenharmony_ci const int procfd_path_size = 317662306a36Sopenharmony_ci snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 317962306a36Sopenharmony_ci return open(procfd_path, open_flags); 318062306a36Sopenharmony_ci} 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ciTEST_F_FORK(layout1, proc_unlinked_file) 318362306a36Sopenharmony_ci{ 318462306a36Sopenharmony_ci const struct rule rules[] = { 318562306a36Sopenharmony_ci { 318662306a36Sopenharmony_ci .path = file1_s1d2, 318762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 318862306a36Sopenharmony_ci }, 318962306a36Sopenharmony_ci {}, 319062306a36Sopenharmony_ci }; 319162306a36Sopenharmony_ci int reg_fd, proc_fd; 319262306a36Sopenharmony_ci const int ruleset_fd = create_ruleset( 319362306a36Sopenharmony_ci _metadata, 319462306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 319562306a36Sopenharmony_ci rules); 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 319862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 319962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 320262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 320362306a36Sopenharmony_ci reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 320462306a36Sopenharmony_ci ASSERT_LE(0, reg_fd); 320562306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file1_s1d2)); 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 320862306a36Sopenharmony_ci ASSERT_LE(0, proc_fd); 320962306a36Sopenharmony_ci ASSERT_EQ(0, close(proc_fd)); 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 321262306a36Sopenharmony_ci ASSERT_EQ(-1, proc_fd) 321362306a36Sopenharmony_ci { 321462306a36Sopenharmony_ci TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 321562306a36Sopenharmony_ci strerror(errno)); 321662306a36Sopenharmony_ci } 321762306a36Sopenharmony_ci ASSERT_EQ(EACCES, errno); 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci ASSERT_EQ(0, close(reg_fd)); 322062306a36Sopenharmony_ci} 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ciTEST_F_FORK(layout1, proc_pipe) 322362306a36Sopenharmony_ci{ 322462306a36Sopenharmony_ci int proc_fd; 322562306a36Sopenharmony_ci int pipe_fds[2]; 322662306a36Sopenharmony_ci char buf = '\0'; 322762306a36Sopenharmony_ci const struct rule rules[] = { 322862306a36Sopenharmony_ci { 322962306a36Sopenharmony_ci .path = dir_s1d2, 323062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 323162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 323262306a36Sopenharmony_ci }, 323362306a36Sopenharmony_ci {}, 323462306a36Sopenharmony_ci }; 323562306a36Sopenharmony_ci /* Limits read and write access to files tied to the filesystem. */ 323662306a36Sopenharmony_ci const int ruleset_fd = 323762306a36Sopenharmony_ci create_ruleset(_metadata, rules[0].access, rules); 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 324062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 324162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci /* Checks enforcement for normal files. */ 324462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 324562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci /* Checks access to pipes through FD. */ 324862306a36Sopenharmony_ci ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 324962306a36Sopenharmony_ci ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 325062306a36Sopenharmony_ci { 325162306a36Sopenharmony_ci TH_LOG("Failed to write in pipe: %s", strerror(errno)); 325262306a36Sopenharmony_ci } 325362306a36Sopenharmony_ci ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 325462306a36Sopenharmony_ci ASSERT_EQ('.', buf); 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci /* Checks write access to pipe through /proc/self/fd . */ 325762306a36Sopenharmony_ci proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 325862306a36Sopenharmony_ci ASSERT_LE(0, proc_fd); 325962306a36Sopenharmony_ci ASSERT_EQ(1, write(proc_fd, ".", 1)) 326062306a36Sopenharmony_ci { 326162306a36Sopenharmony_ci TH_LOG("Failed to write through /proc/self/fd/%d: %s", 326262306a36Sopenharmony_ci pipe_fds[1], strerror(errno)); 326362306a36Sopenharmony_ci } 326462306a36Sopenharmony_ci ASSERT_EQ(0, close(proc_fd)); 326562306a36Sopenharmony_ci 326662306a36Sopenharmony_ci /* Checks read access to pipe through /proc/self/fd . */ 326762306a36Sopenharmony_ci proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 326862306a36Sopenharmony_ci ASSERT_LE(0, proc_fd); 326962306a36Sopenharmony_ci buf = '\0'; 327062306a36Sopenharmony_ci ASSERT_EQ(1, read(proc_fd, &buf, 1)) 327162306a36Sopenharmony_ci { 327262306a36Sopenharmony_ci TH_LOG("Failed to read through /proc/self/fd/%d: %s", 327362306a36Sopenharmony_ci pipe_fds[1], strerror(errno)); 327462306a36Sopenharmony_ci } 327562306a36Sopenharmony_ci ASSERT_EQ(0, close(proc_fd)); 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci ASSERT_EQ(0, close(pipe_fds[0])); 327862306a36Sopenharmony_ci ASSERT_EQ(0, close(pipe_fds[1])); 327962306a36Sopenharmony_ci} 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci/* Invokes truncate(2) and returns its errno or 0. */ 328262306a36Sopenharmony_cistatic int test_truncate(const char *const path) 328362306a36Sopenharmony_ci{ 328462306a36Sopenharmony_ci if (truncate(path, 10) < 0) 328562306a36Sopenharmony_ci return errno; 328662306a36Sopenharmony_ci return 0; 328762306a36Sopenharmony_ci} 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_ci/* 329062306a36Sopenharmony_ci * Invokes creat(2) and returns its errno or 0. 329162306a36Sopenharmony_ci * Closes the opened file descriptor on success. 329262306a36Sopenharmony_ci */ 329362306a36Sopenharmony_cistatic int test_creat(const char *const path) 329462306a36Sopenharmony_ci{ 329562306a36Sopenharmony_ci int fd = creat(path, 0600); 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci if (fd < 0) 329862306a36Sopenharmony_ci return errno; 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci /* 330162306a36Sopenharmony_ci * Mixing error codes from close(2) and creat(2) should not lead to any 330262306a36Sopenharmony_ci * (access type) confusion for this test. 330362306a36Sopenharmony_ci */ 330462306a36Sopenharmony_ci if (close(fd) < 0) 330562306a36Sopenharmony_ci return errno; 330662306a36Sopenharmony_ci return 0; 330762306a36Sopenharmony_ci} 330862306a36Sopenharmony_ci 330962306a36Sopenharmony_ci/* 331062306a36Sopenharmony_ci * Exercises file truncation when it's not restricted, 331162306a36Sopenharmony_ci * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 331262306a36Sopenharmony_ci */ 331362306a36Sopenharmony_ciTEST_F_FORK(layout1, truncate_unhandled) 331462306a36Sopenharmony_ci{ 331562306a36Sopenharmony_ci const char *const file_r = file1_s1d1; 331662306a36Sopenharmony_ci const char *const file_w = file2_s1d1; 331762306a36Sopenharmony_ci const char *const file_none = file1_s1d2; 331862306a36Sopenharmony_ci const struct rule rules[] = { 331962306a36Sopenharmony_ci { 332062306a36Sopenharmony_ci .path = file_r, 332162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 332262306a36Sopenharmony_ci }, 332362306a36Sopenharmony_ci { 332462306a36Sopenharmony_ci .path = file_w, 332562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 332662306a36Sopenharmony_ci }, 332762306a36Sopenharmony_ci /* Implicitly: No rights for file_none. */ 332862306a36Sopenharmony_ci {}, 332962306a36Sopenharmony_ci }; 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_ci const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 333262306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE; 333362306a36Sopenharmony_ci int ruleset_fd; 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci /* Enable Landlock. */ 333662306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, handled, rules); 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 333962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 334062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci /* 334362306a36Sopenharmony_ci * Checks read right: truncate and open with O_TRUNC work, unless the 334462306a36Sopenharmony_ci * file is attempted to be opened for writing. 334562306a36Sopenharmony_ci */ 334662306a36Sopenharmony_ci EXPECT_EQ(0, test_truncate(file_r)); 334762306a36Sopenharmony_ci EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 334862306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 334962306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_creat(file_r)); 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci /* 335262306a36Sopenharmony_ci * Checks write right: truncate and open with O_TRUNC work, unless the 335362306a36Sopenharmony_ci * file is attempted to be opened for reading. 335462306a36Sopenharmony_ci */ 335562306a36Sopenharmony_ci EXPECT_EQ(0, test_truncate(file_w)); 335662306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 335762306a36Sopenharmony_ci EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 335862306a36Sopenharmony_ci EXPECT_EQ(0, test_creat(file_w)); 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci /* 336162306a36Sopenharmony_ci * Checks "no rights" case: truncate works but all open attempts fail, 336262306a36Sopenharmony_ci * including creat. 336362306a36Sopenharmony_ci */ 336462306a36Sopenharmony_ci EXPECT_EQ(0, test_truncate(file_none)); 336562306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 336662306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 336762306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_creat(file_none)); 336862306a36Sopenharmony_ci} 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ciTEST_F_FORK(layout1, truncate) 337162306a36Sopenharmony_ci{ 337262306a36Sopenharmony_ci const char *const file_rwt = file1_s1d1; 337362306a36Sopenharmony_ci const char *const file_rw = file2_s1d1; 337462306a36Sopenharmony_ci const char *const file_rt = file1_s1d2; 337562306a36Sopenharmony_ci const char *const file_t = file2_s1d2; 337662306a36Sopenharmony_ci const char *const file_none = file1_s1d3; 337762306a36Sopenharmony_ci const char *const dir_t = dir_s2d1; 337862306a36Sopenharmony_ci const char *const file_in_dir_t = file1_s2d1; 337962306a36Sopenharmony_ci const char *const dir_w = dir_s3d1; 338062306a36Sopenharmony_ci const char *const file_in_dir_w = file1_s3d1; 338162306a36Sopenharmony_ci const struct rule rules[] = { 338262306a36Sopenharmony_ci { 338362306a36Sopenharmony_ci .path = file_rwt, 338462306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 338562306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE | 338662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_TRUNCATE, 338762306a36Sopenharmony_ci }, 338862306a36Sopenharmony_ci { 338962306a36Sopenharmony_ci .path = file_rw, 339062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 339162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 339262306a36Sopenharmony_ci }, 339362306a36Sopenharmony_ci { 339462306a36Sopenharmony_ci .path = file_rt, 339562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 339662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_TRUNCATE, 339762306a36Sopenharmony_ci }, 339862306a36Sopenharmony_ci { 339962306a36Sopenharmony_ci .path = file_t, 340062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_TRUNCATE, 340162306a36Sopenharmony_ci }, 340262306a36Sopenharmony_ci /* Implicitly: No access rights for file_none. */ 340362306a36Sopenharmony_ci { 340462306a36Sopenharmony_ci .path = dir_t, 340562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_TRUNCATE, 340662306a36Sopenharmony_ci }, 340762306a36Sopenharmony_ci { 340862306a36Sopenharmony_ci .path = dir_w, 340962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 341062306a36Sopenharmony_ci }, 341162306a36Sopenharmony_ci {}, 341262306a36Sopenharmony_ci }; 341362306a36Sopenharmony_ci const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 341462306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE | 341562306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_TRUNCATE; 341662306a36Sopenharmony_ci int ruleset_fd; 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_ci /* Enable Landlock. */ 341962306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, handled, rules); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 342262306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 342362306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci /* Checks read, write and truncate rights: truncation works. */ 342662306a36Sopenharmony_ci EXPECT_EQ(0, test_truncate(file_rwt)); 342762306a36Sopenharmony_ci EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 342862306a36Sopenharmony_ci EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_ci /* Checks read and write rights: no truncate variant works. */ 343162306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_truncate(file_rw)); 343262306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 343362306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci /* 343662306a36Sopenharmony_ci * Checks read and truncate rights: truncation works. 343762306a36Sopenharmony_ci * 343862306a36Sopenharmony_ci * Note: Files can get truncated using open() even with O_RDONLY. 343962306a36Sopenharmony_ci */ 344062306a36Sopenharmony_ci EXPECT_EQ(0, test_truncate(file_rt)); 344162306a36Sopenharmony_ci EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 344262306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci /* Checks truncate right: truncate works, but can't open file. */ 344562306a36Sopenharmony_ci EXPECT_EQ(0, test_truncate(file_t)); 344662306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 344762306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_ci /* Checks "no rights" case: No form of truncation works. */ 345062306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_truncate(file_none)); 345162306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 345262306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci /* 345562306a36Sopenharmony_ci * Checks truncate right on directory: truncate works on contained 345662306a36Sopenharmony_ci * files. 345762306a36Sopenharmony_ci */ 345862306a36Sopenharmony_ci EXPECT_EQ(0, test_truncate(file_in_dir_t)); 345962306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 346062306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci /* 346362306a36Sopenharmony_ci * Checks creat in dir_w: This requires the truncate right when 346462306a36Sopenharmony_ci * overwriting an existing file, but does not require it when the file 346562306a36Sopenharmony_ci * is new. 346662306a36Sopenharmony_ci */ 346762306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci ASSERT_EQ(0, unlink(file_in_dir_w)); 347062306a36Sopenharmony_ci EXPECT_EQ(0, test_creat(file_in_dir_w)); 347162306a36Sopenharmony_ci} 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci/* Invokes ftruncate(2) and returns its errno or 0. */ 347462306a36Sopenharmony_cistatic int test_ftruncate(int fd) 347562306a36Sopenharmony_ci{ 347662306a36Sopenharmony_ci if (ftruncate(fd, 10) < 0) 347762306a36Sopenharmony_ci return errno; 347862306a36Sopenharmony_ci return 0; 347962306a36Sopenharmony_ci} 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ciTEST_F_FORK(layout1, ftruncate) 348262306a36Sopenharmony_ci{ 348362306a36Sopenharmony_ci /* 348462306a36Sopenharmony_ci * This test opens a new file descriptor at different stages of 348562306a36Sopenharmony_ci * Landlock restriction: 348662306a36Sopenharmony_ci * 348762306a36Sopenharmony_ci * without restriction: ftruncate works 348862306a36Sopenharmony_ci * something else but truncate restricted: ftruncate works 348962306a36Sopenharmony_ci * truncate restricted and permitted: ftruncate works 349062306a36Sopenharmony_ci * truncate restricted and not permitted: ftruncate fails 349162306a36Sopenharmony_ci * 349262306a36Sopenharmony_ci * Whether this works or not is expected to depend on the time when the 349362306a36Sopenharmony_ci * FD was opened, not to depend on the time when ftruncate() was 349462306a36Sopenharmony_ci * called. 349562306a36Sopenharmony_ci */ 349662306a36Sopenharmony_ci const char *const path = file1_s1d1; 349762306a36Sopenharmony_ci const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 349862306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE; 349962306a36Sopenharmony_ci const struct rule layer1[] = { 350062306a36Sopenharmony_ci { 350162306a36Sopenharmony_ci .path = path, 350262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 350362306a36Sopenharmony_ci }, 350462306a36Sopenharmony_ci {}, 350562306a36Sopenharmony_ci }; 350662306a36Sopenharmony_ci const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 350762306a36Sopenharmony_ci const struct rule layer2[] = { 350862306a36Sopenharmony_ci { 350962306a36Sopenharmony_ci .path = path, 351062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_TRUNCATE, 351162306a36Sopenharmony_ci }, 351262306a36Sopenharmony_ci {}, 351362306a36Sopenharmony_ci }; 351462306a36Sopenharmony_ci const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 351562306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE; 351662306a36Sopenharmony_ci const struct rule layer3[] = { 351762306a36Sopenharmony_ci { 351862306a36Sopenharmony_ci .path = path, 351962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 352062306a36Sopenharmony_ci }, 352162306a36Sopenharmony_ci {}, 352262306a36Sopenharmony_ci }; 352362306a36Sopenharmony_ci int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; 352462306a36Sopenharmony_ci 352562306a36Sopenharmony_ci fd_layer0 = open(path, O_WRONLY); 352662306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer0)); 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, handled1, layer1); 352962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 353062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 353162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 353262306a36Sopenharmony_ci 353362306a36Sopenharmony_ci fd_layer1 = open(path, O_WRONLY); 353462306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer0)); 353562306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer1)); 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, handled2, layer2); 353862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 353962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 354062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci fd_layer2 = open(path, O_WRONLY); 354362306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer0)); 354462306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer1)); 354562306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer2)); 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, handled3, layer3); 354862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 354962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 355062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci fd_layer3 = open(path, O_WRONLY); 355362306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer0)); 355462306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer1)); 355562306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd_layer2)); 355662306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_ci ASSERT_EQ(0, close(fd_layer0)); 355962306a36Sopenharmony_ci ASSERT_EQ(0, close(fd_layer1)); 356062306a36Sopenharmony_ci ASSERT_EQ(0, close(fd_layer2)); 356162306a36Sopenharmony_ci ASSERT_EQ(0, close(fd_layer3)); 356262306a36Sopenharmony_ci} 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci/* clang-format off */ 356562306a36Sopenharmony_ciFIXTURE(ftruncate) {}; 356662306a36Sopenharmony_ci/* clang-format on */ 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_ciFIXTURE_SETUP(ftruncate) 356962306a36Sopenharmony_ci{ 357062306a36Sopenharmony_ci prepare_layout(_metadata); 357162306a36Sopenharmony_ci create_file(_metadata, file1_s1d1); 357262306a36Sopenharmony_ci} 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ciFIXTURE_TEARDOWN(ftruncate) 357562306a36Sopenharmony_ci{ 357662306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(file1_s1d1)); 357762306a36Sopenharmony_ci cleanup_layout(_metadata); 357862306a36Sopenharmony_ci} 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ciFIXTURE_VARIANT(ftruncate) 358162306a36Sopenharmony_ci{ 358262306a36Sopenharmony_ci const __u64 handled; 358362306a36Sopenharmony_ci const __u64 permitted; 358462306a36Sopenharmony_ci const int expected_open_result; 358562306a36Sopenharmony_ci const int expected_ftruncate_result; 358662306a36Sopenharmony_ci}; 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_ci/* clang-format off */ 358962306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, w_w) { 359062306a36Sopenharmony_ci /* clang-format on */ 359162306a36Sopenharmony_ci .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 359262306a36Sopenharmony_ci .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 359362306a36Sopenharmony_ci .expected_open_result = 0, 359462306a36Sopenharmony_ci .expected_ftruncate_result = 0, 359562306a36Sopenharmony_ci}; 359662306a36Sopenharmony_ci 359762306a36Sopenharmony_ci/* clang-format off */ 359862306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, t_t) { 359962306a36Sopenharmony_ci /* clang-format on */ 360062306a36Sopenharmony_ci .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 360162306a36Sopenharmony_ci .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 360262306a36Sopenharmony_ci .expected_open_result = 0, 360362306a36Sopenharmony_ci .expected_ftruncate_result = 0, 360462306a36Sopenharmony_ci}; 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci/* clang-format off */ 360762306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, wt_w) { 360862306a36Sopenharmony_ci /* clang-format on */ 360962306a36Sopenharmony_ci .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 361062306a36Sopenharmony_ci .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 361162306a36Sopenharmony_ci .expected_open_result = 0, 361262306a36Sopenharmony_ci .expected_ftruncate_result = EACCES, 361362306a36Sopenharmony_ci}; 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_ci/* clang-format off */ 361662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 361762306a36Sopenharmony_ci /* clang-format on */ 361862306a36Sopenharmony_ci .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 361962306a36Sopenharmony_ci .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE | 362062306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_TRUNCATE, 362162306a36Sopenharmony_ci .expected_open_result = 0, 362262306a36Sopenharmony_ci .expected_ftruncate_result = 0, 362362306a36Sopenharmony_ci}; 362462306a36Sopenharmony_ci 362562306a36Sopenharmony_ci/* clang-format off */ 362662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(ftruncate, wt_t) { 362762306a36Sopenharmony_ci /* clang-format on */ 362862306a36Sopenharmony_ci .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 362962306a36Sopenharmony_ci .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 363062306a36Sopenharmony_ci .expected_open_result = EACCES, 363162306a36Sopenharmony_ci}; 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ciTEST_F_FORK(ftruncate, open_and_ftruncate) 363462306a36Sopenharmony_ci{ 363562306a36Sopenharmony_ci const char *const path = file1_s1d1; 363662306a36Sopenharmony_ci const struct rule rules[] = { 363762306a36Sopenharmony_ci { 363862306a36Sopenharmony_ci .path = path, 363962306a36Sopenharmony_ci .access = variant->permitted, 364062306a36Sopenharmony_ci }, 364162306a36Sopenharmony_ci {}, 364262306a36Sopenharmony_ci }; 364362306a36Sopenharmony_ci int fd, ruleset_fd; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci /* Enable Landlock. */ 364662306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 364762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 364862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 364962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 365062306a36Sopenharmony_ci 365162306a36Sopenharmony_ci fd = open(path, O_WRONLY); 365262306a36Sopenharmony_ci EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 365362306a36Sopenharmony_ci if (fd >= 0) { 365462306a36Sopenharmony_ci EXPECT_EQ(variant->expected_ftruncate_result, 365562306a36Sopenharmony_ci test_ftruncate(fd)); 365662306a36Sopenharmony_ci ASSERT_EQ(0, close(fd)); 365762306a36Sopenharmony_ci } 365862306a36Sopenharmony_ci} 365962306a36Sopenharmony_ci 366062306a36Sopenharmony_ciTEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 366162306a36Sopenharmony_ci{ 366262306a36Sopenharmony_ci int child, fd, status; 366362306a36Sopenharmony_ci int socket_fds[2]; 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 366662306a36Sopenharmony_ci socket_fds)); 366762306a36Sopenharmony_ci 366862306a36Sopenharmony_ci child = fork(); 366962306a36Sopenharmony_ci ASSERT_LE(0, child); 367062306a36Sopenharmony_ci if (child == 0) { 367162306a36Sopenharmony_ci /* 367262306a36Sopenharmony_ci * Enables Landlock in the child process, open a file descriptor 367362306a36Sopenharmony_ci * where truncation is forbidden and send it to the 367462306a36Sopenharmony_ci * non-landlocked parent process. 367562306a36Sopenharmony_ci */ 367662306a36Sopenharmony_ci const char *const path = file1_s1d1; 367762306a36Sopenharmony_ci const struct rule rules[] = { 367862306a36Sopenharmony_ci { 367962306a36Sopenharmony_ci .path = path, 368062306a36Sopenharmony_ci .access = variant->permitted, 368162306a36Sopenharmony_ci }, 368262306a36Sopenharmony_ci {}, 368362306a36Sopenharmony_ci }; 368462306a36Sopenharmony_ci int fd, ruleset_fd; 368562306a36Sopenharmony_ci 368662306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 368762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 368862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 368962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci fd = open(path, O_WRONLY); 369262306a36Sopenharmony_ci ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci if (fd >= 0) { 369562306a36Sopenharmony_ci ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 369662306a36Sopenharmony_ci ASSERT_EQ(0, close(fd)); 369762306a36Sopenharmony_ci } 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci ASSERT_EQ(0, close(socket_fds[0])); 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 370262306a36Sopenharmony_ci return; 370362306a36Sopenharmony_ci } 370462306a36Sopenharmony_ci 370562306a36Sopenharmony_ci if (variant->expected_open_result == 0) { 370662306a36Sopenharmony_ci fd = recv_fd(socket_fds[1]); 370762306a36Sopenharmony_ci ASSERT_LE(0, fd); 370862306a36Sopenharmony_ci 370962306a36Sopenharmony_ci EXPECT_EQ(variant->expected_ftruncate_result, 371062306a36Sopenharmony_ci test_ftruncate(fd)); 371162306a36Sopenharmony_ci ASSERT_EQ(0, close(fd)); 371262306a36Sopenharmony_ci } 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci ASSERT_EQ(child, waitpid(child, &status, 0)); 371562306a36Sopenharmony_ci ASSERT_EQ(1, WIFEXITED(status)); 371662306a36Sopenharmony_ci ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci ASSERT_EQ(0, close(socket_fds[0])); 371962306a36Sopenharmony_ci ASSERT_EQ(0, close(socket_fds[1])); 372062306a36Sopenharmony_ci} 372162306a36Sopenharmony_ci 372262306a36Sopenharmony_ciTEST(memfd_ftruncate) 372362306a36Sopenharmony_ci{ 372462306a36Sopenharmony_ci int fd; 372562306a36Sopenharmony_ci 372662306a36Sopenharmony_ci fd = memfd_create("name", MFD_CLOEXEC); 372762306a36Sopenharmony_ci ASSERT_LE(0, fd); 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci /* 373062306a36Sopenharmony_ci * Checks that ftruncate is permitted on file descriptors that are 373162306a36Sopenharmony_ci * created in ways other than open(2). 373262306a36Sopenharmony_ci */ 373362306a36Sopenharmony_ci EXPECT_EQ(0, test_ftruncate(fd)); 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci ASSERT_EQ(0, close(fd)); 373662306a36Sopenharmony_ci} 373762306a36Sopenharmony_ci 373862306a36Sopenharmony_ci/* clang-format off */ 373962306a36Sopenharmony_ciFIXTURE(layout1_bind) {}; 374062306a36Sopenharmony_ci/* clang-format on */ 374162306a36Sopenharmony_ci 374262306a36Sopenharmony_ciFIXTURE_SETUP(layout1_bind) 374362306a36Sopenharmony_ci{ 374462306a36Sopenharmony_ci prepare_layout(_metadata); 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ci create_layout1(_metadata); 374762306a36Sopenharmony_ci 374862306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 374962306a36Sopenharmony_ci ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 375062306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 375162306a36Sopenharmony_ci} 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout1_bind) 375462306a36Sopenharmony_ci{ 375562306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 375662306a36Sopenharmony_ci EXPECT_EQ(0, umount(dir_s2d2)); 375762306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_ci remove_layout1(_metadata); 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci cleanup_layout(_metadata); 376262306a36Sopenharmony_ci} 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_cistatic const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 376562306a36Sopenharmony_cistatic const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 376662306a36Sopenharmony_ci 376762306a36Sopenharmony_ci/* 376862306a36Sopenharmony_ci * layout1_bind hierarchy: 376962306a36Sopenharmony_ci * 377062306a36Sopenharmony_ci * tmp 377162306a36Sopenharmony_ci * ├── s1d1 377262306a36Sopenharmony_ci * │ ├── f1 377362306a36Sopenharmony_ci * │ ├── f2 377462306a36Sopenharmony_ci * │ └── s1d2 377562306a36Sopenharmony_ci * │ ├── f1 377662306a36Sopenharmony_ci * │ ├── f2 377762306a36Sopenharmony_ci * │ └── s1d3 377862306a36Sopenharmony_ci * │ ├── f1 377962306a36Sopenharmony_ci * │ └── f2 378062306a36Sopenharmony_ci * ├── s2d1 378162306a36Sopenharmony_ci * │ ├── f1 378262306a36Sopenharmony_ci * │ └── s2d2 378362306a36Sopenharmony_ci * │ ├── f1 378462306a36Sopenharmony_ci * │ ├── f2 378562306a36Sopenharmony_ci * │ └── s1d3 378662306a36Sopenharmony_ci * │ ├── f1 378762306a36Sopenharmony_ci * │ └── f2 378862306a36Sopenharmony_ci * └── s3d1 378962306a36Sopenharmony_ci * └── s3d2 379062306a36Sopenharmony_ci * └── s3d3 379162306a36Sopenharmony_ci */ 379262306a36Sopenharmony_ci 379362306a36Sopenharmony_ciTEST_F_FORK(layout1_bind, no_restriction) 379462306a36Sopenharmony_ci{ 379562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 379662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 379762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 379862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 379962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 380062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 380362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 380462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 380562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 380662306a36Sopenharmony_ci ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 380762306a36Sopenharmony_ci ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 381062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 381362306a36Sopenharmony_ci} 381462306a36Sopenharmony_ci 381562306a36Sopenharmony_ciTEST_F_FORK(layout1_bind, same_content_same_file) 381662306a36Sopenharmony_ci{ 381762306a36Sopenharmony_ci /* 381862306a36Sopenharmony_ci * Sets access right on parent directories of both source and 381962306a36Sopenharmony_ci * destination mount points. 382062306a36Sopenharmony_ci */ 382162306a36Sopenharmony_ci const struct rule layer1_parent[] = { 382262306a36Sopenharmony_ci { 382362306a36Sopenharmony_ci .path = dir_s1d1, 382462306a36Sopenharmony_ci .access = ACCESS_RO, 382562306a36Sopenharmony_ci }, 382662306a36Sopenharmony_ci { 382762306a36Sopenharmony_ci .path = dir_s2d1, 382862306a36Sopenharmony_ci .access = ACCESS_RW, 382962306a36Sopenharmony_ci }, 383062306a36Sopenharmony_ci {}, 383162306a36Sopenharmony_ci }; 383262306a36Sopenharmony_ci /* 383362306a36Sopenharmony_ci * Sets access rights on the same bind-mounted directories. The result 383462306a36Sopenharmony_ci * should be ACCESS_RW for both directories, but not both hierarchies 383562306a36Sopenharmony_ci * because of the first layer. 383662306a36Sopenharmony_ci */ 383762306a36Sopenharmony_ci const struct rule layer2_mount_point[] = { 383862306a36Sopenharmony_ci { 383962306a36Sopenharmony_ci .path = dir_s1d2, 384062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 384162306a36Sopenharmony_ci }, 384262306a36Sopenharmony_ci { 384362306a36Sopenharmony_ci .path = dir_s2d2, 384462306a36Sopenharmony_ci .access = ACCESS_RW, 384562306a36Sopenharmony_ci }, 384662306a36Sopenharmony_ci {}, 384762306a36Sopenharmony_ci }; 384862306a36Sopenharmony_ci /* Only allow read-access to the s1d3 hierarchies. */ 384962306a36Sopenharmony_ci const struct rule layer3_source[] = { 385062306a36Sopenharmony_ci { 385162306a36Sopenharmony_ci .path = dir_s1d3, 385262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 385362306a36Sopenharmony_ci }, 385462306a36Sopenharmony_ci {}, 385562306a36Sopenharmony_ci }; 385662306a36Sopenharmony_ci /* Removes all access rights. */ 385762306a36Sopenharmony_ci const struct rule layer4_destination[] = { 385862306a36Sopenharmony_ci { 385962306a36Sopenharmony_ci .path = bind_file1_s1d3, 386062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 386162306a36Sopenharmony_ci }, 386262306a36Sopenharmony_ci {}, 386362306a36Sopenharmony_ci }; 386462306a36Sopenharmony_ci int ruleset_fd; 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_ci /* Sets rules for the parent directories. */ 386762306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 386862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 386962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 387062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 387162306a36Sopenharmony_ci 387262306a36Sopenharmony_ci /* Checks source hierarchy. */ 387362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 387462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 387562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 387862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 387962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_ci /* Checks destination hierarchy. */ 388262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 388362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 388462306a36Sopenharmony_ci 388562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 388662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci /* Sets rules for the mount points. */ 388962306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 389062306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 389162306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 389262306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 389362306a36Sopenharmony_ci 389462306a36Sopenharmony_ci /* Checks source hierarchy. */ 389562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 389662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 389762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 390062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 390162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci /* Checks destination hierarchy. */ 390462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 390562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 390662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 390962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 391062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ci /* Sets a (shared) rule only on the source. */ 391362306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 391462306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 391562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 391662306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci /* Checks source hierarchy. */ 391962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 392062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 392162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 392462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 392562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 392662306a36Sopenharmony_ci 392762306a36Sopenharmony_ci /* Checks destination hierarchy. */ 392862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 392962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 393062306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 393362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 393462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci /* Sets a (shared) rule only on the destination. */ 393762306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 393862306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 393962306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 394062306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 394162306a36Sopenharmony_ci 394262306a36Sopenharmony_ci /* Checks source hierarchy. */ 394362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 394462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 394562306a36Sopenharmony_ci 394662306a36Sopenharmony_ci /* Checks destination hierarchy. */ 394762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 394862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 394962306a36Sopenharmony_ci} 395062306a36Sopenharmony_ci 395162306a36Sopenharmony_ciTEST_F_FORK(layout1_bind, reparent_cross_mount) 395262306a36Sopenharmony_ci{ 395362306a36Sopenharmony_ci const struct rule layer1[] = { 395462306a36Sopenharmony_ci { 395562306a36Sopenharmony_ci /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 395662306a36Sopenharmony_ci .path = dir_s2d1, 395762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_REFER, 395862306a36Sopenharmony_ci }, 395962306a36Sopenharmony_ci { 396062306a36Sopenharmony_ci .path = bind_dir_s1d3, 396162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_EXECUTE, 396262306a36Sopenharmony_ci }, 396362306a36Sopenharmony_ci {}, 396462306a36Sopenharmony_ci }; 396562306a36Sopenharmony_ci int ruleset_fd = create_ruleset( 396662306a36Sopenharmony_ci _metadata, 396762306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 397062306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 397162306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 397262306a36Sopenharmony_ci 397362306a36Sopenharmony_ci /* Checks basic denied move. */ 397462306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 397562306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 397662306a36Sopenharmony_ci 397762306a36Sopenharmony_ci /* Checks real cross-mount move (Landlock is not involved). */ 397862306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 397962306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci /* Checks move that will give more accesses. */ 398262306a36Sopenharmony_ci ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 398362306a36Sopenharmony_ci ASSERT_EQ(EXDEV, errno); 398462306a36Sopenharmony_ci 398562306a36Sopenharmony_ci /* Checks legitimate downgrade move. */ 398662306a36Sopenharmony_ci ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 398762306a36Sopenharmony_ci} 398862306a36Sopenharmony_ci 398962306a36Sopenharmony_ci#define LOWER_BASE TMP_DIR "/lower" 399062306a36Sopenharmony_ci#define LOWER_DATA LOWER_BASE "/data" 399162306a36Sopenharmony_cistatic const char lower_fl1[] = LOWER_DATA "/fl1"; 399262306a36Sopenharmony_cistatic const char lower_dl1[] = LOWER_DATA "/dl1"; 399362306a36Sopenharmony_cistatic const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 399462306a36Sopenharmony_cistatic const char lower_fo1[] = LOWER_DATA "/fo1"; 399562306a36Sopenharmony_cistatic const char lower_do1[] = LOWER_DATA "/do1"; 399662306a36Sopenharmony_cistatic const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 399762306a36Sopenharmony_cistatic const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 399862306a36Sopenharmony_ci 399962306a36Sopenharmony_cistatic const char (*lower_base_files[])[] = { 400062306a36Sopenharmony_ci &lower_fl1, 400162306a36Sopenharmony_ci &lower_fo1, 400262306a36Sopenharmony_ci NULL, 400362306a36Sopenharmony_ci}; 400462306a36Sopenharmony_cistatic const char (*lower_base_directories[])[] = { 400562306a36Sopenharmony_ci &lower_dl1, 400662306a36Sopenharmony_ci &lower_do1, 400762306a36Sopenharmony_ci NULL, 400862306a36Sopenharmony_ci}; 400962306a36Sopenharmony_cistatic const char (*lower_sub_files[])[] = { 401062306a36Sopenharmony_ci &lower_dl1_fl2, 401162306a36Sopenharmony_ci &lower_do1_fo2, 401262306a36Sopenharmony_ci &lower_do1_fl3, 401362306a36Sopenharmony_ci NULL, 401462306a36Sopenharmony_ci}; 401562306a36Sopenharmony_ci 401662306a36Sopenharmony_ci#define UPPER_BASE TMP_DIR "/upper" 401762306a36Sopenharmony_ci#define UPPER_DATA UPPER_BASE "/data" 401862306a36Sopenharmony_ci#define UPPER_WORK UPPER_BASE "/work" 401962306a36Sopenharmony_cistatic const char upper_fu1[] = UPPER_DATA "/fu1"; 402062306a36Sopenharmony_cistatic const char upper_du1[] = UPPER_DATA "/du1"; 402162306a36Sopenharmony_cistatic const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 402262306a36Sopenharmony_cistatic const char upper_fo1[] = UPPER_DATA "/fo1"; 402362306a36Sopenharmony_cistatic const char upper_do1[] = UPPER_DATA "/do1"; 402462306a36Sopenharmony_cistatic const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 402562306a36Sopenharmony_cistatic const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 402662306a36Sopenharmony_ci 402762306a36Sopenharmony_cistatic const char (*upper_base_files[])[] = { 402862306a36Sopenharmony_ci &upper_fu1, 402962306a36Sopenharmony_ci &upper_fo1, 403062306a36Sopenharmony_ci NULL, 403162306a36Sopenharmony_ci}; 403262306a36Sopenharmony_cistatic const char (*upper_base_directories[])[] = { 403362306a36Sopenharmony_ci &upper_du1, 403462306a36Sopenharmony_ci &upper_do1, 403562306a36Sopenharmony_ci NULL, 403662306a36Sopenharmony_ci}; 403762306a36Sopenharmony_cistatic const char (*upper_sub_files[])[] = { 403862306a36Sopenharmony_ci &upper_du1_fu2, 403962306a36Sopenharmony_ci &upper_do1_fo2, 404062306a36Sopenharmony_ci &upper_do1_fu3, 404162306a36Sopenharmony_ci NULL, 404262306a36Sopenharmony_ci}; 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci#define MERGE_BASE TMP_DIR "/merge" 404562306a36Sopenharmony_ci#define MERGE_DATA MERGE_BASE "/data" 404662306a36Sopenharmony_cistatic const char merge_fl1[] = MERGE_DATA "/fl1"; 404762306a36Sopenharmony_cistatic const char merge_dl1[] = MERGE_DATA "/dl1"; 404862306a36Sopenharmony_cistatic const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 404962306a36Sopenharmony_cistatic const char merge_fu1[] = MERGE_DATA "/fu1"; 405062306a36Sopenharmony_cistatic const char merge_du1[] = MERGE_DATA "/du1"; 405162306a36Sopenharmony_cistatic const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 405262306a36Sopenharmony_cistatic const char merge_fo1[] = MERGE_DATA "/fo1"; 405362306a36Sopenharmony_cistatic const char merge_do1[] = MERGE_DATA "/do1"; 405462306a36Sopenharmony_cistatic const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 405562306a36Sopenharmony_cistatic const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 405662306a36Sopenharmony_cistatic const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 405762306a36Sopenharmony_ci 405862306a36Sopenharmony_cistatic const char (*merge_base_files[])[] = { 405962306a36Sopenharmony_ci &merge_fl1, 406062306a36Sopenharmony_ci &merge_fu1, 406162306a36Sopenharmony_ci &merge_fo1, 406262306a36Sopenharmony_ci NULL, 406362306a36Sopenharmony_ci}; 406462306a36Sopenharmony_cistatic const char (*merge_base_directories[])[] = { 406562306a36Sopenharmony_ci &merge_dl1, 406662306a36Sopenharmony_ci &merge_du1, 406762306a36Sopenharmony_ci &merge_do1, 406862306a36Sopenharmony_ci NULL, 406962306a36Sopenharmony_ci}; 407062306a36Sopenharmony_cistatic const char (*merge_sub_files[])[] = { 407162306a36Sopenharmony_ci &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 407262306a36Sopenharmony_ci &merge_do1_fl3, &merge_do1_fu3, NULL, 407362306a36Sopenharmony_ci}; 407462306a36Sopenharmony_ci 407562306a36Sopenharmony_ci/* 407662306a36Sopenharmony_ci * layout2_overlay hierarchy: 407762306a36Sopenharmony_ci * 407862306a36Sopenharmony_ci * tmp 407962306a36Sopenharmony_ci * ├── lower 408062306a36Sopenharmony_ci * │ └── data 408162306a36Sopenharmony_ci * │ ├── dl1 408262306a36Sopenharmony_ci * │ │ └── fl2 408362306a36Sopenharmony_ci * │ ├── do1 408462306a36Sopenharmony_ci * │ │ ├── fl3 408562306a36Sopenharmony_ci * │ │ └── fo2 408662306a36Sopenharmony_ci * │ ├── fl1 408762306a36Sopenharmony_ci * │ └── fo1 408862306a36Sopenharmony_ci * ├── merge 408962306a36Sopenharmony_ci * │ └── data 409062306a36Sopenharmony_ci * │ ├── dl1 409162306a36Sopenharmony_ci * │ │ └── fl2 409262306a36Sopenharmony_ci * │ ├── do1 409362306a36Sopenharmony_ci * │ │ ├── fl3 409462306a36Sopenharmony_ci * │ │ ├── fo2 409562306a36Sopenharmony_ci * │ │ └── fu3 409662306a36Sopenharmony_ci * │ ├── du1 409762306a36Sopenharmony_ci * │ │ └── fu2 409862306a36Sopenharmony_ci * │ ├── fl1 409962306a36Sopenharmony_ci * │ ├── fo1 410062306a36Sopenharmony_ci * │ └── fu1 410162306a36Sopenharmony_ci * └── upper 410262306a36Sopenharmony_ci * ├── data 410362306a36Sopenharmony_ci * │ ├── do1 410462306a36Sopenharmony_ci * │ │ ├── fo2 410562306a36Sopenharmony_ci * │ │ └── fu3 410662306a36Sopenharmony_ci * │ ├── du1 410762306a36Sopenharmony_ci * │ │ └── fu2 410862306a36Sopenharmony_ci * │ ├── fo1 410962306a36Sopenharmony_ci * │ └── fu1 411062306a36Sopenharmony_ci * └── work 411162306a36Sopenharmony_ci * └── work 411262306a36Sopenharmony_ci */ 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ciFIXTURE(layout2_overlay) 411562306a36Sopenharmony_ci{ 411662306a36Sopenharmony_ci bool skip_test; 411762306a36Sopenharmony_ci}; 411862306a36Sopenharmony_ci 411962306a36Sopenharmony_ciFIXTURE_SETUP(layout2_overlay) 412062306a36Sopenharmony_ci{ 412162306a36Sopenharmony_ci if (!supports_filesystem("overlay")) { 412262306a36Sopenharmony_ci self->skip_test = true; 412362306a36Sopenharmony_ci SKIP(return, "overlayfs is not supported (setup)"); 412462306a36Sopenharmony_ci } 412562306a36Sopenharmony_ci 412662306a36Sopenharmony_ci prepare_layout(_metadata); 412762306a36Sopenharmony_ci 412862306a36Sopenharmony_ci create_directory(_metadata, LOWER_BASE); 412962306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 413062306a36Sopenharmony_ci /* Creates tmpfs mount points to get deterministic overlayfs. */ 413162306a36Sopenharmony_ci ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 413262306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 413362306a36Sopenharmony_ci create_file(_metadata, lower_fl1); 413462306a36Sopenharmony_ci create_file(_metadata, lower_dl1_fl2); 413562306a36Sopenharmony_ci create_file(_metadata, lower_fo1); 413662306a36Sopenharmony_ci create_file(_metadata, lower_do1_fo2); 413762306a36Sopenharmony_ci create_file(_metadata, lower_do1_fl3); 413862306a36Sopenharmony_ci 413962306a36Sopenharmony_ci create_directory(_metadata, UPPER_BASE); 414062306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 414162306a36Sopenharmony_ci ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 414262306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 414362306a36Sopenharmony_ci create_file(_metadata, upper_fu1); 414462306a36Sopenharmony_ci create_file(_metadata, upper_du1_fu2); 414562306a36Sopenharmony_ci create_file(_metadata, upper_fo1); 414662306a36Sopenharmony_ci create_file(_metadata, upper_do1_fo2); 414762306a36Sopenharmony_ci create_file(_metadata, upper_do1_fu3); 414862306a36Sopenharmony_ci ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 414962306a36Sopenharmony_ci 415062306a36Sopenharmony_ci create_directory(_metadata, MERGE_DATA); 415162306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 415262306a36Sopenharmony_ci set_cap(_metadata, CAP_DAC_OVERRIDE); 415362306a36Sopenharmony_ci ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 415462306a36Sopenharmony_ci "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 415562306a36Sopenharmony_ci ",workdir=" UPPER_WORK)); 415662306a36Sopenharmony_ci clear_cap(_metadata, CAP_DAC_OVERRIDE); 415762306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 415862306a36Sopenharmony_ci} 415962306a36Sopenharmony_ci 416062306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout2_overlay) 416162306a36Sopenharmony_ci{ 416262306a36Sopenharmony_ci if (self->skip_test) 416362306a36Sopenharmony_ci SKIP(return, "overlayfs is not supported (teardown)"); 416462306a36Sopenharmony_ci 416562306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(lower_do1_fl3)); 416662306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 416762306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(lower_fl1)); 416862306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(lower_do1_fo2)); 416962306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(lower_fo1)); 417062306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 417162306a36Sopenharmony_ci EXPECT_EQ(0, umount(LOWER_BASE)); 417262306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 417362306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(LOWER_BASE)); 417462306a36Sopenharmony_ci 417562306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(upper_do1_fu3)); 417662306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(upper_du1_fu2)); 417762306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(upper_fu1)); 417862306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(upper_do1_fo2)); 417962306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(upper_fo1)); 418062306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 418162306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 418262306a36Sopenharmony_ci EXPECT_EQ(0, umount(UPPER_BASE)); 418362306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 418462306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(UPPER_BASE)); 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 418762306a36Sopenharmony_ci EXPECT_EQ(0, umount(MERGE_DATA)); 418862306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 418962306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(MERGE_DATA)); 419062306a36Sopenharmony_ci 419162306a36Sopenharmony_ci cleanup_layout(_metadata); 419262306a36Sopenharmony_ci} 419362306a36Sopenharmony_ci 419462306a36Sopenharmony_ciTEST_F_FORK(layout2_overlay, no_restriction) 419562306a36Sopenharmony_ci{ 419662306a36Sopenharmony_ci if (self->skip_test) 419762306a36Sopenharmony_ci SKIP(return, "overlayfs is not supported (test)"); 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 420062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 420162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 420262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 420362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 420462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 420562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 420862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 420962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 421062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 421162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 421262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 421362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 421462306a36Sopenharmony_ci 421562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 421662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 421762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 421862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 421962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 422062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 422162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 422262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 422362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 422462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 422562306a36Sopenharmony_ci ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 422662306a36Sopenharmony_ci} 422762306a36Sopenharmony_ci 422862306a36Sopenharmony_ci#define for_each_path(path_list, path_entry, i) \ 422962306a36Sopenharmony_ci for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 423062306a36Sopenharmony_ci path_entry = *path_list[++i]) 423162306a36Sopenharmony_ci 423262306a36Sopenharmony_ciTEST_F_FORK(layout2_overlay, same_content_different_file) 423362306a36Sopenharmony_ci{ 423462306a36Sopenharmony_ci /* Sets access right on parent directories of both layers. */ 423562306a36Sopenharmony_ci const struct rule layer1_base[] = { 423662306a36Sopenharmony_ci { 423762306a36Sopenharmony_ci .path = LOWER_BASE, 423862306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 423962306a36Sopenharmony_ci }, 424062306a36Sopenharmony_ci { 424162306a36Sopenharmony_ci .path = UPPER_BASE, 424262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 424362306a36Sopenharmony_ci }, 424462306a36Sopenharmony_ci { 424562306a36Sopenharmony_ci .path = MERGE_BASE, 424662306a36Sopenharmony_ci .access = ACCESS_RW, 424762306a36Sopenharmony_ci }, 424862306a36Sopenharmony_ci {}, 424962306a36Sopenharmony_ci }; 425062306a36Sopenharmony_ci const struct rule layer2_data[] = { 425162306a36Sopenharmony_ci { 425262306a36Sopenharmony_ci .path = LOWER_DATA, 425362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 425462306a36Sopenharmony_ci }, 425562306a36Sopenharmony_ci { 425662306a36Sopenharmony_ci .path = UPPER_DATA, 425762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 425862306a36Sopenharmony_ci }, 425962306a36Sopenharmony_ci { 426062306a36Sopenharmony_ci .path = MERGE_DATA, 426162306a36Sopenharmony_ci .access = ACCESS_RW, 426262306a36Sopenharmony_ci }, 426362306a36Sopenharmony_ci {}, 426462306a36Sopenharmony_ci }; 426562306a36Sopenharmony_ci /* Sets access right on directories inside both layers. */ 426662306a36Sopenharmony_ci const struct rule layer3_subdirs[] = { 426762306a36Sopenharmony_ci { 426862306a36Sopenharmony_ci .path = lower_dl1, 426962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 427062306a36Sopenharmony_ci }, 427162306a36Sopenharmony_ci { 427262306a36Sopenharmony_ci .path = lower_do1, 427362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 427462306a36Sopenharmony_ci }, 427562306a36Sopenharmony_ci { 427662306a36Sopenharmony_ci .path = upper_du1, 427762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 427862306a36Sopenharmony_ci }, 427962306a36Sopenharmony_ci { 428062306a36Sopenharmony_ci .path = upper_do1, 428162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 428262306a36Sopenharmony_ci }, 428362306a36Sopenharmony_ci { 428462306a36Sopenharmony_ci .path = merge_dl1, 428562306a36Sopenharmony_ci .access = ACCESS_RW, 428662306a36Sopenharmony_ci }, 428762306a36Sopenharmony_ci { 428862306a36Sopenharmony_ci .path = merge_du1, 428962306a36Sopenharmony_ci .access = ACCESS_RW, 429062306a36Sopenharmony_ci }, 429162306a36Sopenharmony_ci { 429262306a36Sopenharmony_ci .path = merge_do1, 429362306a36Sopenharmony_ci .access = ACCESS_RW, 429462306a36Sopenharmony_ci }, 429562306a36Sopenharmony_ci {}, 429662306a36Sopenharmony_ci }; 429762306a36Sopenharmony_ci /* Tighten access rights to the files. */ 429862306a36Sopenharmony_ci const struct rule layer4_files[] = { 429962306a36Sopenharmony_ci { 430062306a36Sopenharmony_ci .path = lower_dl1_fl2, 430162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 430262306a36Sopenharmony_ci }, 430362306a36Sopenharmony_ci { 430462306a36Sopenharmony_ci .path = lower_do1_fo2, 430562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 430662306a36Sopenharmony_ci }, 430762306a36Sopenharmony_ci { 430862306a36Sopenharmony_ci .path = lower_do1_fl3, 430962306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 431062306a36Sopenharmony_ci }, 431162306a36Sopenharmony_ci { 431262306a36Sopenharmony_ci .path = upper_du1_fu2, 431362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 431462306a36Sopenharmony_ci }, 431562306a36Sopenharmony_ci { 431662306a36Sopenharmony_ci .path = upper_do1_fo2, 431762306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 431862306a36Sopenharmony_ci }, 431962306a36Sopenharmony_ci { 432062306a36Sopenharmony_ci .path = upper_do1_fu3, 432162306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 432262306a36Sopenharmony_ci }, 432362306a36Sopenharmony_ci { 432462306a36Sopenharmony_ci .path = merge_dl1_fl2, 432562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 432662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 432762306a36Sopenharmony_ci }, 432862306a36Sopenharmony_ci { 432962306a36Sopenharmony_ci .path = merge_du1_fu2, 433062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 433162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 433262306a36Sopenharmony_ci }, 433362306a36Sopenharmony_ci { 433462306a36Sopenharmony_ci .path = merge_do1_fo2, 433562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 433662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 433762306a36Sopenharmony_ci }, 433862306a36Sopenharmony_ci { 433962306a36Sopenharmony_ci .path = merge_do1_fl3, 434062306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 434162306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 434262306a36Sopenharmony_ci }, 434362306a36Sopenharmony_ci { 434462306a36Sopenharmony_ci .path = merge_do1_fu3, 434562306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 434662306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 434762306a36Sopenharmony_ci }, 434862306a36Sopenharmony_ci {}, 434962306a36Sopenharmony_ci }; 435062306a36Sopenharmony_ci const struct rule layer5_merge_only[] = { 435162306a36Sopenharmony_ci { 435262306a36Sopenharmony_ci .path = MERGE_DATA, 435362306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE | 435462306a36Sopenharmony_ci LANDLOCK_ACCESS_FS_WRITE_FILE, 435562306a36Sopenharmony_ci }, 435662306a36Sopenharmony_ci {}, 435762306a36Sopenharmony_ci }; 435862306a36Sopenharmony_ci int ruleset_fd; 435962306a36Sopenharmony_ci size_t i; 436062306a36Sopenharmony_ci const char *path_entry; 436162306a36Sopenharmony_ci 436262306a36Sopenharmony_ci if (self->skip_test) 436362306a36Sopenharmony_ci SKIP(return, "overlayfs is not supported (test)"); 436462306a36Sopenharmony_ci 436562306a36Sopenharmony_ci /* Sets rules on base directories (i.e. outside overlay scope). */ 436662306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 436762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 436862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 436962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 437062306a36Sopenharmony_ci 437162306a36Sopenharmony_ci /* Checks lower layer. */ 437262306a36Sopenharmony_ci for_each_path(lower_base_files, path_entry, i) { 437362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 437462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 437562306a36Sopenharmony_ci } 437662306a36Sopenharmony_ci for_each_path(lower_base_directories, path_entry, i) { 437762306a36Sopenharmony_ci ASSERT_EQ(EACCES, 437862306a36Sopenharmony_ci test_open(path_entry, O_RDONLY | O_DIRECTORY)); 437962306a36Sopenharmony_ci } 438062306a36Sopenharmony_ci for_each_path(lower_sub_files, path_entry, i) { 438162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 438262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 438362306a36Sopenharmony_ci } 438462306a36Sopenharmony_ci /* Checks upper layer. */ 438562306a36Sopenharmony_ci for_each_path(upper_base_files, path_entry, i) { 438662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 438762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 438862306a36Sopenharmony_ci } 438962306a36Sopenharmony_ci for_each_path(upper_base_directories, path_entry, i) { 439062306a36Sopenharmony_ci ASSERT_EQ(EACCES, 439162306a36Sopenharmony_ci test_open(path_entry, O_RDONLY | O_DIRECTORY)); 439262306a36Sopenharmony_ci } 439362306a36Sopenharmony_ci for_each_path(upper_sub_files, path_entry, i) { 439462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 439562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 439662306a36Sopenharmony_ci } 439762306a36Sopenharmony_ci /* 439862306a36Sopenharmony_ci * Checks that access rights are independent from the lower and upper 439962306a36Sopenharmony_ci * layers: write access to upper files viewed through the merge point 440062306a36Sopenharmony_ci * is still allowed, and write access to lower file viewed (and copied) 440162306a36Sopenharmony_ci * through the merge point is still allowed. 440262306a36Sopenharmony_ci */ 440362306a36Sopenharmony_ci for_each_path(merge_base_files, path_entry, i) { 440462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 440562306a36Sopenharmony_ci } 440662306a36Sopenharmony_ci for_each_path(merge_base_directories, path_entry, i) { 440762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 440862306a36Sopenharmony_ci } 440962306a36Sopenharmony_ci for_each_path(merge_sub_files, path_entry, i) { 441062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 441162306a36Sopenharmony_ci } 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_ci /* Sets rules on data directories (i.e. inside overlay scope). */ 441462306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 441562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 441662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 441762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 441862306a36Sopenharmony_ci 441962306a36Sopenharmony_ci /* Checks merge. */ 442062306a36Sopenharmony_ci for_each_path(merge_base_files, path_entry, i) { 442162306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 442262306a36Sopenharmony_ci } 442362306a36Sopenharmony_ci for_each_path(merge_base_directories, path_entry, i) { 442462306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 442562306a36Sopenharmony_ci } 442662306a36Sopenharmony_ci for_each_path(merge_sub_files, path_entry, i) { 442762306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 442862306a36Sopenharmony_ci } 442962306a36Sopenharmony_ci 443062306a36Sopenharmony_ci /* Same checks with tighter rules. */ 443162306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 443262306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 443362306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 443462306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 443562306a36Sopenharmony_ci 443662306a36Sopenharmony_ci /* Checks changes for lower layer. */ 443762306a36Sopenharmony_ci for_each_path(lower_base_files, path_entry, i) { 443862306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 443962306a36Sopenharmony_ci } 444062306a36Sopenharmony_ci /* Checks changes for upper layer. */ 444162306a36Sopenharmony_ci for_each_path(upper_base_files, path_entry, i) { 444262306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 444362306a36Sopenharmony_ci } 444462306a36Sopenharmony_ci /* Checks all merge accesses. */ 444562306a36Sopenharmony_ci for_each_path(merge_base_files, path_entry, i) { 444662306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 444762306a36Sopenharmony_ci } 444862306a36Sopenharmony_ci for_each_path(merge_base_directories, path_entry, i) { 444962306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 445062306a36Sopenharmony_ci } 445162306a36Sopenharmony_ci for_each_path(merge_sub_files, path_entry, i) { 445262306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 445362306a36Sopenharmony_ci } 445462306a36Sopenharmony_ci 445562306a36Sopenharmony_ci /* Sets rules directly on overlayed files. */ 445662306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 445762306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 445862306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 445962306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 446062306a36Sopenharmony_ci 446162306a36Sopenharmony_ci /* Checks unchanged accesses on lower layer. */ 446262306a36Sopenharmony_ci for_each_path(lower_sub_files, path_entry, i) { 446362306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 446462306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 446562306a36Sopenharmony_ci } 446662306a36Sopenharmony_ci /* Checks unchanged accesses on upper layer. */ 446762306a36Sopenharmony_ci for_each_path(upper_sub_files, path_entry, i) { 446862306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 446962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 447062306a36Sopenharmony_ci } 447162306a36Sopenharmony_ci /* Checks all merge accesses. */ 447262306a36Sopenharmony_ci for_each_path(merge_base_files, path_entry, i) { 447362306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 447462306a36Sopenharmony_ci } 447562306a36Sopenharmony_ci for_each_path(merge_base_directories, path_entry, i) { 447662306a36Sopenharmony_ci ASSERT_EQ(EACCES, 447762306a36Sopenharmony_ci test_open(path_entry, O_RDONLY | O_DIRECTORY)); 447862306a36Sopenharmony_ci } 447962306a36Sopenharmony_ci for_each_path(merge_sub_files, path_entry, i) { 448062306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 448162306a36Sopenharmony_ci } 448262306a36Sopenharmony_ci 448362306a36Sopenharmony_ci /* Only allowes access to the merge hierarchy. */ 448462306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 448562306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 448662306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 448762306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci /* Checks new accesses on lower layer. */ 449062306a36Sopenharmony_ci for_each_path(lower_sub_files, path_entry, i) { 449162306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 449262306a36Sopenharmony_ci } 449362306a36Sopenharmony_ci /* Checks new accesses on upper layer. */ 449462306a36Sopenharmony_ci for_each_path(upper_sub_files, path_entry, i) { 449562306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 449662306a36Sopenharmony_ci } 449762306a36Sopenharmony_ci /* Checks all merge accesses. */ 449862306a36Sopenharmony_ci for_each_path(merge_base_files, path_entry, i) { 449962306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 450062306a36Sopenharmony_ci } 450162306a36Sopenharmony_ci for_each_path(merge_base_directories, path_entry, i) { 450262306a36Sopenharmony_ci ASSERT_EQ(EACCES, 450362306a36Sopenharmony_ci test_open(path_entry, O_RDONLY | O_DIRECTORY)); 450462306a36Sopenharmony_ci } 450562306a36Sopenharmony_ci for_each_path(merge_sub_files, path_entry, i) { 450662306a36Sopenharmony_ci ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 450762306a36Sopenharmony_ci } 450862306a36Sopenharmony_ci} 450962306a36Sopenharmony_ci 451062306a36Sopenharmony_ciFIXTURE(layout3_fs) 451162306a36Sopenharmony_ci{ 451262306a36Sopenharmony_ci bool has_created_dir; 451362306a36Sopenharmony_ci bool has_created_file; 451462306a36Sopenharmony_ci char *dir_path; 451562306a36Sopenharmony_ci bool skip_test; 451662306a36Sopenharmony_ci}; 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ciFIXTURE_VARIANT(layout3_fs) 451962306a36Sopenharmony_ci{ 452062306a36Sopenharmony_ci const struct mnt_opt mnt; 452162306a36Sopenharmony_ci const char *const file_path; 452262306a36Sopenharmony_ci unsigned int cwd_fs_magic; 452362306a36Sopenharmony_ci}; 452462306a36Sopenharmony_ci 452562306a36Sopenharmony_ci/* clang-format off */ 452662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 452762306a36Sopenharmony_ci /* clang-format on */ 452862306a36Sopenharmony_ci .mnt = { 452962306a36Sopenharmony_ci .type = "tmpfs", 453062306a36Sopenharmony_ci .data = MNT_TMP_DATA, 453162306a36Sopenharmony_ci }, 453262306a36Sopenharmony_ci .file_path = file1_s1d1, 453362306a36Sopenharmony_ci}; 453462306a36Sopenharmony_ci 453562306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 453662306a36Sopenharmony_ci .mnt = { 453762306a36Sopenharmony_ci .type = "ramfs", 453862306a36Sopenharmony_ci .data = "mode=700", 453962306a36Sopenharmony_ci }, 454062306a36Sopenharmony_ci .file_path = TMP_DIR "/dir/file", 454162306a36Sopenharmony_ci}; 454262306a36Sopenharmony_ci 454362306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 454462306a36Sopenharmony_ci .mnt = { 454562306a36Sopenharmony_ci .type = "cgroup2", 454662306a36Sopenharmony_ci }, 454762306a36Sopenharmony_ci .file_path = TMP_DIR "/test/cgroup.procs", 454862306a36Sopenharmony_ci}; 454962306a36Sopenharmony_ci 455062306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, proc) { 455162306a36Sopenharmony_ci .mnt = { 455262306a36Sopenharmony_ci .type = "proc", 455362306a36Sopenharmony_ci }, 455462306a36Sopenharmony_ci .file_path = TMP_DIR "/self/status", 455562306a36Sopenharmony_ci}; 455662306a36Sopenharmony_ci 455762306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 455862306a36Sopenharmony_ci .mnt = { 455962306a36Sopenharmony_ci .type = "sysfs", 456062306a36Sopenharmony_ci }, 456162306a36Sopenharmony_ci .file_path = TMP_DIR "/kernel/notes", 456262306a36Sopenharmony_ci}; 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 456562306a36Sopenharmony_ci .mnt = { 456662306a36Sopenharmony_ci .source = TMP_DIR, 456762306a36Sopenharmony_ci .flags = MS_BIND, 456862306a36Sopenharmony_ci }, 456962306a36Sopenharmony_ci .file_path = TMP_DIR "/dir/file", 457062306a36Sopenharmony_ci .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 457162306a36Sopenharmony_ci}; 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_ciFIXTURE_SETUP(layout3_fs) 457462306a36Sopenharmony_ci{ 457562306a36Sopenharmony_ci struct stat statbuf; 457662306a36Sopenharmony_ci const char *slash; 457762306a36Sopenharmony_ci size_t dir_len; 457862306a36Sopenharmony_ci 457962306a36Sopenharmony_ci if (!supports_filesystem(variant->mnt.type) || 458062306a36Sopenharmony_ci !cwd_matches_fs(variant->cwd_fs_magic)) { 458162306a36Sopenharmony_ci self->skip_test = true; 458262306a36Sopenharmony_ci SKIP(return, "this filesystem is not supported (setup)"); 458362306a36Sopenharmony_ci } 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci slash = strrchr(variant->file_path, '/'); 458662306a36Sopenharmony_ci ASSERT_NE(slash, NULL); 458762306a36Sopenharmony_ci dir_len = (size_t)slash - (size_t)variant->file_path; 458862306a36Sopenharmony_ci ASSERT_LT(0, dir_len); 458962306a36Sopenharmony_ci self->dir_path = malloc(dir_len + 1); 459062306a36Sopenharmony_ci self->dir_path[dir_len] = '\0'; 459162306a36Sopenharmony_ci strncpy(self->dir_path, variant->file_path, dir_len); 459262306a36Sopenharmony_ci 459362306a36Sopenharmony_ci prepare_layout_opt(_metadata, &variant->mnt); 459462306a36Sopenharmony_ci 459562306a36Sopenharmony_ci /* Creates directory when required. */ 459662306a36Sopenharmony_ci if (stat(self->dir_path, &statbuf)) { 459762306a36Sopenharmony_ci set_cap(_metadata, CAP_DAC_OVERRIDE); 459862306a36Sopenharmony_ci EXPECT_EQ(0, mkdir(self->dir_path, 0700)) 459962306a36Sopenharmony_ci { 460062306a36Sopenharmony_ci TH_LOG("Failed to create directory \"%s\": %s", 460162306a36Sopenharmony_ci self->dir_path, strerror(errno)); 460262306a36Sopenharmony_ci free(self->dir_path); 460362306a36Sopenharmony_ci self->dir_path = NULL; 460462306a36Sopenharmony_ci } 460562306a36Sopenharmony_ci self->has_created_dir = true; 460662306a36Sopenharmony_ci clear_cap(_metadata, CAP_DAC_OVERRIDE); 460762306a36Sopenharmony_ci } 460862306a36Sopenharmony_ci 460962306a36Sopenharmony_ci /* Creates file when required. */ 461062306a36Sopenharmony_ci if (stat(variant->file_path, &statbuf)) { 461162306a36Sopenharmony_ci int fd; 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_ci set_cap(_metadata, CAP_DAC_OVERRIDE); 461462306a36Sopenharmony_ci fd = creat(variant->file_path, 0600); 461562306a36Sopenharmony_ci EXPECT_LE(0, fd) 461662306a36Sopenharmony_ci { 461762306a36Sopenharmony_ci TH_LOG("Failed to create file \"%s\": %s", 461862306a36Sopenharmony_ci variant->file_path, strerror(errno)); 461962306a36Sopenharmony_ci } 462062306a36Sopenharmony_ci EXPECT_EQ(0, close(fd)); 462162306a36Sopenharmony_ci self->has_created_file = true; 462262306a36Sopenharmony_ci clear_cap(_metadata, CAP_DAC_OVERRIDE); 462362306a36Sopenharmony_ci } 462462306a36Sopenharmony_ci} 462562306a36Sopenharmony_ci 462662306a36Sopenharmony_ciFIXTURE_TEARDOWN(layout3_fs) 462762306a36Sopenharmony_ci{ 462862306a36Sopenharmony_ci if (self->skip_test) 462962306a36Sopenharmony_ci SKIP(return, "this filesystem is not supported (teardown)"); 463062306a36Sopenharmony_ci 463162306a36Sopenharmony_ci if (self->has_created_file) { 463262306a36Sopenharmony_ci set_cap(_metadata, CAP_DAC_OVERRIDE); 463362306a36Sopenharmony_ci /* 463462306a36Sopenharmony_ci * Don't check for error because the file might already 463562306a36Sopenharmony_ci * have been removed (cf. release_inode test). 463662306a36Sopenharmony_ci */ 463762306a36Sopenharmony_ci unlink(variant->file_path); 463862306a36Sopenharmony_ci clear_cap(_metadata, CAP_DAC_OVERRIDE); 463962306a36Sopenharmony_ci } 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_ci if (self->has_created_dir) { 464262306a36Sopenharmony_ci set_cap(_metadata, CAP_DAC_OVERRIDE); 464362306a36Sopenharmony_ci /* 464462306a36Sopenharmony_ci * Don't check for error because the directory might already 464562306a36Sopenharmony_ci * have been removed (cf. release_inode test). 464662306a36Sopenharmony_ci */ 464762306a36Sopenharmony_ci rmdir(self->dir_path); 464862306a36Sopenharmony_ci clear_cap(_metadata, CAP_DAC_OVERRIDE); 464962306a36Sopenharmony_ci } 465062306a36Sopenharmony_ci free(self->dir_path); 465162306a36Sopenharmony_ci self->dir_path = NULL; 465262306a36Sopenharmony_ci 465362306a36Sopenharmony_ci cleanup_layout(_metadata); 465462306a36Sopenharmony_ci} 465562306a36Sopenharmony_ci 465662306a36Sopenharmony_cistatic void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 465762306a36Sopenharmony_ci FIXTURE_DATA(layout3_fs) * self, 465862306a36Sopenharmony_ci const FIXTURE_VARIANT(layout3_fs) * variant, 465962306a36Sopenharmony_ci const char *const rule_path) 466062306a36Sopenharmony_ci{ 466162306a36Sopenharmony_ci const struct rule layer1_allow_read_file[] = { 466262306a36Sopenharmony_ci { 466362306a36Sopenharmony_ci .path = rule_path, 466462306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_FILE, 466562306a36Sopenharmony_ci }, 466662306a36Sopenharmony_ci {}, 466762306a36Sopenharmony_ci }; 466862306a36Sopenharmony_ci const struct landlock_ruleset_attr layer2_deny_everything_attr = { 466962306a36Sopenharmony_ci .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 467062306a36Sopenharmony_ci }; 467162306a36Sopenharmony_ci const char *const dev_null_path = "/dev/null"; 467262306a36Sopenharmony_ci int ruleset_fd; 467362306a36Sopenharmony_ci 467462306a36Sopenharmony_ci if (self->skip_test) 467562306a36Sopenharmony_ci SKIP(return, "this filesystem is not supported (test)"); 467662306a36Sopenharmony_ci 467762306a36Sopenharmony_ci /* Checks without Landlock. */ 467862306a36Sopenharmony_ci EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 467962306a36Sopenharmony_ci EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 468062306a36Sopenharmony_ci 468162306a36Sopenharmony_ci ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 468262306a36Sopenharmony_ci layer1_allow_read_file); 468362306a36Sopenharmony_ci EXPECT_LE(0, ruleset_fd); 468462306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 468562306a36Sopenharmony_ci EXPECT_EQ(0, close(ruleset_fd)); 468662306a36Sopenharmony_ci 468762306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 468862306a36Sopenharmony_ci EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 468962306a36Sopenharmony_ci 469062306a36Sopenharmony_ci /* Forbids directory reading. */ 469162306a36Sopenharmony_ci ruleset_fd = 469262306a36Sopenharmony_ci landlock_create_ruleset(&layer2_deny_everything_attr, 469362306a36Sopenharmony_ci sizeof(layer2_deny_everything_attr), 0); 469462306a36Sopenharmony_ci EXPECT_LE(0, ruleset_fd); 469562306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 469662306a36Sopenharmony_ci EXPECT_EQ(0, close(ruleset_fd)); 469762306a36Sopenharmony_ci 469862306a36Sopenharmony_ci /* Checks with Landlock and forbidden access. */ 469962306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 470062306a36Sopenharmony_ci EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 470162306a36Sopenharmony_ci} 470262306a36Sopenharmony_ci 470362306a36Sopenharmony_ci/* Matrix of tests to check file hierarchy evaluation. */ 470462306a36Sopenharmony_ci 470562306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_dir_parent) 470662306a36Sopenharmony_ci{ 470762306a36Sopenharmony_ci /* The current directory must not be the root for this test. */ 470862306a36Sopenharmony_ci layer3_fs_tag_inode(_metadata, self, variant, "."); 470962306a36Sopenharmony_ci} 471062306a36Sopenharmony_ci 471162306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 471262306a36Sopenharmony_ci{ 471362306a36Sopenharmony_ci layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 471462306a36Sopenharmony_ci} 471562306a36Sopenharmony_ci 471662306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_dir_child) 471762306a36Sopenharmony_ci{ 471862306a36Sopenharmony_ci layer3_fs_tag_inode(_metadata, self, variant, self->dir_path); 471962306a36Sopenharmony_ci} 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, tag_inode_file) 472262306a36Sopenharmony_ci{ 472362306a36Sopenharmony_ci layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 472462306a36Sopenharmony_ci} 472562306a36Sopenharmony_ci 472662306a36Sopenharmony_ci/* Light version of layout1.release_inodes */ 472762306a36Sopenharmony_ciTEST_F_FORK(layout3_fs, release_inodes) 472862306a36Sopenharmony_ci{ 472962306a36Sopenharmony_ci const struct rule layer1[] = { 473062306a36Sopenharmony_ci { 473162306a36Sopenharmony_ci .path = TMP_DIR, 473262306a36Sopenharmony_ci .access = LANDLOCK_ACCESS_FS_READ_DIR, 473362306a36Sopenharmony_ci }, 473462306a36Sopenharmony_ci {}, 473562306a36Sopenharmony_ci }; 473662306a36Sopenharmony_ci int ruleset_fd; 473762306a36Sopenharmony_ci 473862306a36Sopenharmony_ci if (self->skip_test) 473962306a36Sopenharmony_ci SKIP(return, "this filesystem is not supported (test)"); 474062306a36Sopenharmony_ci 474162306a36Sopenharmony_ci /* Clean up for the teardown to not fail. */ 474262306a36Sopenharmony_ci if (self->has_created_file) 474362306a36Sopenharmony_ci EXPECT_EQ(0, remove_path(variant->file_path)); 474462306a36Sopenharmony_ci 474562306a36Sopenharmony_ci if (self->has_created_dir) 474662306a36Sopenharmony_ci /* Don't check for error because of cgroup specificities. */ 474762306a36Sopenharmony_ci remove_path(self->dir_path); 474862306a36Sopenharmony_ci 474962306a36Sopenharmony_ci ruleset_fd = 475062306a36Sopenharmony_ci create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 475162306a36Sopenharmony_ci ASSERT_LE(0, ruleset_fd); 475262306a36Sopenharmony_ci 475362306a36Sopenharmony_ci /* Unmount the filesystem while it is being used by a ruleset. */ 475462306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 475562306a36Sopenharmony_ci ASSERT_EQ(0, umount(TMP_DIR)); 475662306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 475762306a36Sopenharmony_ci 475862306a36Sopenharmony_ci /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 475962306a36Sopenharmony_ci set_cap(_metadata, CAP_SYS_ADMIN); 476062306a36Sopenharmony_ci ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 476162306a36Sopenharmony_ci clear_cap(_metadata, CAP_SYS_ADMIN); 476262306a36Sopenharmony_ci 476362306a36Sopenharmony_ci enforce_ruleset(_metadata, ruleset_fd); 476462306a36Sopenharmony_ci ASSERT_EQ(0, close(ruleset_fd)); 476562306a36Sopenharmony_ci 476662306a36Sopenharmony_ci /* Checks that access to the new mount point is denied. */ 476762306a36Sopenharmony_ci ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 476862306a36Sopenharmony_ci} 476962306a36Sopenharmony_ci 477062306a36Sopenharmony_ciTEST_HARNESS_MAIN 4771