Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/landlock: Add audit tests for filesystem

Test all filesystem blockers, including events with several records, and
record with several blockers:
- fs.execute
- fs.write_file
- fs.read_file
- fs_read_dir
- fs.remove_dir
- fs.remove_file
- fs.make_char
- fs.make_dir
- fs.make_reg
- fs.make_sock
- fs.make_fifo
- fs.make_block
- fs.make_sym
- fs.refer
- fs.truncate
- fs.ioctl_dev
- fs.change_topology

Cc: Günther Noack <gnoack@google.com>
Cc: Paul Moore <paul@paul-moore.com>
Link: https://lore.kernel.org/r/20250320190717.2287696-27-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>

+645
+35
tools/testing/selftests/landlock/audit.h
··· 208 208 return audit_request(fd, &msg, NULL); 209 209 } 210 210 211 + /* Returns a pointer to the last filled character of @dst, which is `\0`. */ 212 + static __maybe_unused char *regex_escape(const char *const src, char *dst, 213 + size_t dst_size) 214 + { 215 + char *d = dst; 216 + 217 + for (const char *s = src; *s; s++) { 218 + switch (*s) { 219 + case '$': 220 + case '*': 221 + case '.': 222 + case '[': 223 + case '\\': 224 + case ']': 225 + case '^': 226 + if (d >= dst + dst_size - 2) 227 + return (char *)-ENOMEM; 228 + 229 + *d++ = '\\'; 230 + *d++ = *s; 231 + break; 232 + default: 233 + if (d >= dst + dst_size - 1) 234 + return (char *)-ENOMEM; 235 + 236 + *d++ = *s; 237 + } 238 + } 239 + if (d >= dst + dst_size - 1) 240 + return (char *)-ENOMEM; 241 + 242 + *d = '\0'; 243 + return d; 244 + } 245 + 211 246 /* 212 247 * @domain_id: The domain ID extracted from the audit message (if the first part 213 248 * of @pattern is REGEX_LANDLOCK_PREFIX). It is set to 0 if the domain ID is
+16
tools/testing/selftests/landlock/common.h
··· 208 208 } 209 209 } 210 210 211 + static void __maybe_unused 212 + drop_access_rights(struct __test_metadata *const _metadata, 213 + const struct landlock_ruleset_attr *const ruleset_attr) 214 + { 215 + int ruleset_fd; 216 + 217 + ruleset_fd = 218 + landlock_create_ruleset(ruleset_attr, sizeof(*ruleset_attr), 0); 219 + EXPECT_LE(0, ruleset_fd) 220 + { 221 + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 222 + } 223 + enforce_ruleset(_metadata, ruleset_fd); 224 + EXPECT_EQ(0, close(ruleset_fd)); 225 + } 226 + 211 227 struct protocol_variant { 212 228 int domain; 213 229 int type;
+594
tools/testing/selftests/landlock/fs_test.c
··· 41 41 #define _ASM_GENERIC_FCNTL_H 42 42 #include <linux/fcntl.h> 43 43 44 + #include "audit.h" 44 45 #include "common.h" 45 46 46 47 #ifndef renameat2 ··· 5553 5552 5554 5553 /* Checks that access to the new mount point is denied. */ 5555 5554 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 5555 + } 5556 + 5557 + static int matches_log_fs_extra(struct __test_metadata *const _metadata, 5558 + int audit_fd, const char *const blockers, 5559 + const char *const path, const char *const extra) 5560 + { 5561 + static const char log_template[] = REGEX_LANDLOCK_PREFIX 5562 + " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$"; 5563 + char *absolute_path = NULL; 5564 + size_t log_match_remaining = sizeof(log_template) + strlen(blockers) + 5565 + PATH_MAX * 2 + 5566 + (extra ? strlen(extra) : 0) + 1; 5567 + char log_match[log_match_remaining]; 5568 + char *log_match_cursor = log_match; 5569 + size_t chunk_len; 5570 + 5571 + chunk_len = snprintf(log_match_cursor, log_match_remaining, 5572 + REGEX_LANDLOCK_PREFIX " blockers=%s path=\"", 5573 + blockers); 5574 + if (chunk_len < 0 || chunk_len >= log_match_remaining) 5575 + return -E2BIG; 5576 + 5577 + /* 5578 + * It is assume that absolute_path does not contain control characters nor 5579 + * spaces, see audit_string_contains_control(). 5580 + */ 5581 + absolute_path = realpath(path, NULL); 5582 + if (!absolute_path) 5583 + return -errno; 5584 + 5585 + log_match_remaining -= chunk_len; 5586 + log_match_cursor += chunk_len; 5587 + log_match_cursor = regex_escape(absolute_path, log_match_cursor, 5588 + log_match_remaining); 5589 + free(absolute_path); 5590 + if (log_match_cursor < 0) 5591 + return (long long)log_match_cursor; 5592 + 5593 + log_match_remaining -= log_match_cursor - log_match; 5594 + chunk_len = snprintf(log_match_cursor, log_match_remaining, 5595 + "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$", 5596 + extra ?: ""); 5597 + if (chunk_len < 0 || chunk_len >= log_match_remaining) 5598 + return -E2BIG; 5599 + 5600 + return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, 5601 + NULL); 5602 + } 5603 + 5604 + static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd, 5605 + const char *const blockers, const char *const path) 5606 + { 5607 + return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL); 5608 + } 5609 + 5610 + FIXTURE(audit_layout1) 5611 + { 5612 + struct audit_filter audit_filter; 5613 + int audit_fd; 5614 + }; 5615 + 5616 + FIXTURE_SETUP(audit_layout1) 5617 + { 5618 + prepare_layout(_metadata); 5619 + 5620 + create_layout1(_metadata); 5621 + 5622 + set_cap(_metadata, CAP_AUDIT_CONTROL); 5623 + self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 5624 + EXPECT_LE(0, self->audit_fd); 5625 + disable_caps(_metadata); 5626 + } 5627 + 5628 + FIXTURE_TEARDOWN_PARENT(audit_layout1) 5629 + { 5630 + remove_layout1(_metadata); 5631 + 5632 + cleanup_layout(_metadata); 5633 + 5634 + EXPECT_EQ(0, audit_cleanup(-1, NULL)); 5635 + } 5636 + 5637 + TEST_F(audit_layout1, execute_make) 5638 + { 5639 + struct audit_records records; 5640 + 5641 + copy_file(_metadata, bin_true, file1_s1d1); 5642 + test_execute(_metadata, 0, file1_s1d1); 5643 + test_check_exec(_metadata, 0, file1_s1d1); 5644 + 5645 + drop_access_rights(_metadata, 5646 + &(struct landlock_ruleset_attr){ 5647 + .handled_access_fs = 5648 + LANDLOCK_ACCESS_FS_EXECUTE, 5649 + }); 5650 + 5651 + test_execute(_metadata, EACCES, file1_s1d1); 5652 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 5653 + file1_s1d1)); 5654 + test_check_exec(_metadata, EACCES, file1_s1d1); 5655 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 5656 + file1_s1d1)); 5657 + 5658 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5659 + EXPECT_EQ(0, records.access); 5660 + EXPECT_EQ(0, records.domain); 5661 + } 5662 + 5663 + /* 5664 + * Using a set of handled/denied access rights make it possible to check that 5665 + * only the blocked ones are logged. 5666 + */ 5667 + 5668 + /* clang-format off */ 5669 + static const __u64 access_fs_16 = 5670 + LANDLOCK_ACCESS_FS_EXECUTE | 5671 + LANDLOCK_ACCESS_FS_WRITE_FILE | 5672 + LANDLOCK_ACCESS_FS_READ_FILE | 5673 + LANDLOCK_ACCESS_FS_READ_DIR | 5674 + LANDLOCK_ACCESS_FS_REMOVE_DIR | 5675 + LANDLOCK_ACCESS_FS_REMOVE_FILE | 5676 + LANDLOCK_ACCESS_FS_MAKE_CHAR | 5677 + LANDLOCK_ACCESS_FS_MAKE_DIR | 5678 + LANDLOCK_ACCESS_FS_MAKE_REG | 5679 + LANDLOCK_ACCESS_FS_MAKE_SOCK | 5680 + LANDLOCK_ACCESS_FS_MAKE_FIFO | 5681 + LANDLOCK_ACCESS_FS_MAKE_BLOCK | 5682 + LANDLOCK_ACCESS_FS_MAKE_SYM | 5683 + LANDLOCK_ACCESS_FS_REFER | 5684 + LANDLOCK_ACCESS_FS_TRUNCATE | 5685 + LANDLOCK_ACCESS_FS_IOCTL_DEV; 5686 + /* clang-format on */ 5687 + 5688 + TEST_F(audit_layout1, execute_read) 5689 + { 5690 + struct audit_records records; 5691 + 5692 + copy_file(_metadata, bin_true, file1_s1d1); 5693 + test_execute(_metadata, 0, file1_s1d1); 5694 + test_check_exec(_metadata, 0, file1_s1d1); 5695 + 5696 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5697 + .handled_access_fs = access_fs_16, 5698 + }); 5699 + 5700 + /* 5701 + * The only difference with the previous audit_layout1.execute_read test is 5702 + * the extra ",fs\\.read_file" blocked by the executable file. 5703 + */ 5704 + test_execute(_metadata, EACCES, file1_s1d1); 5705 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5706 + "fs\\.execute,fs\\.read_file", file1_s1d1)); 5707 + test_check_exec(_metadata, EACCES, file1_s1d1); 5708 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5709 + "fs\\.execute,fs\\.read_file", file1_s1d1)); 5710 + 5711 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5712 + EXPECT_EQ(0, records.access); 5713 + EXPECT_EQ(0, records.domain); 5714 + } 5715 + 5716 + TEST_F(audit_layout1, write_file) 5717 + { 5718 + struct audit_records records; 5719 + 5720 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5721 + .handled_access_fs = access_fs_16, 5722 + }); 5723 + 5724 + EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 5725 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5726 + "fs\\.write_file", file1_s1d1)); 5727 + 5728 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5729 + EXPECT_EQ(0, records.access); 5730 + EXPECT_EQ(1, records.domain); 5731 + } 5732 + 5733 + TEST_F(audit_layout1, read_file) 5734 + { 5735 + struct audit_records records; 5736 + 5737 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5738 + .handled_access_fs = access_fs_16, 5739 + }); 5740 + 5741 + EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 5742 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file", 5743 + file1_s1d1)); 5744 + 5745 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5746 + EXPECT_EQ(0, records.access); 5747 + EXPECT_EQ(1, records.domain); 5748 + } 5749 + 5750 + TEST_F(audit_layout1, read_dir) 5751 + { 5752 + struct audit_records records; 5753 + 5754 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5755 + .handled_access_fs = access_fs_16, 5756 + }); 5757 + 5758 + EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY)); 5759 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir", 5760 + dir_s1d1)); 5761 + 5762 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5763 + EXPECT_EQ(0, records.access); 5764 + EXPECT_EQ(1, records.domain); 5765 + } 5766 + 5767 + TEST_F(audit_layout1, remove_dir) 5768 + { 5769 + struct audit_records records; 5770 + 5771 + EXPECT_EQ(0, unlink(file1_s1d3)); 5772 + EXPECT_EQ(0, unlink(file2_s1d3)); 5773 + 5774 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5775 + .handled_access_fs = access_fs_16, 5776 + }); 5777 + 5778 + EXPECT_EQ(-1, rmdir(dir_s1d3)); 5779 + EXPECT_EQ(EACCES, errno); 5780 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5781 + "fs\\.remove_dir", dir_s1d2)); 5782 + 5783 + EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 5784 + EXPECT_EQ(EACCES, errno); 5785 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5786 + "fs\\.remove_dir", dir_s1d2)); 5787 + 5788 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5789 + EXPECT_EQ(0, records.access); 5790 + EXPECT_EQ(0, records.domain); 5791 + } 5792 + 5793 + TEST_F(audit_layout1, remove_file) 5794 + { 5795 + struct audit_records records; 5796 + 5797 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5798 + .handled_access_fs = access_fs_16, 5799 + }); 5800 + 5801 + EXPECT_EQ(-1, unlink(file1_s1d3)); 5802 + EXPECT_EQ(EACCES, errno); 5803 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5804 + "fs\\.remove_file", dir_s1d3)); 5805 + 5806 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5807 + EXPECT_EQ(0, records.access); 5808 + EXPECT_EQ(1, records.domain); 5809 + } 5810 + 5811 + TEST_F(audit_layout1, make_char) 5812 + { 5813 + struct audit_records records; 5814 + 5815 + EXPECT_EQ(0, unlink(file1_s1d3)); 5816 + 5817 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5818 + .handled_access_fs = access_fs_16, 5819 + }); 5820 + 5821 + EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, 0)); 5822 + EXPECT_EQ(EACCES, errno); 5823 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char", 5824 + dir_s1d3)); 5825 + 5826 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5827 + EXPECT_EQ(0, records.access); 5828 + EXPECT_EQ(1, records.domain); 5829 + } 5830 + 5831 + TEST_F(audit_layout1, make_dir) 5832 + { 5833 + struct audit_records records; 5834 + 5835 + EXPECT_EQ(0, unlink(file1_s1d3)); 5836 + 5837 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5838 + .handled_access_fs = access_fs_16, 5839 + }); 5840 + 5841 + EXPECT_EQ(-1, mkdir(file1_s1d3, 0755)); 5842 + EXPECT_EQ(EACCES, errno); 5843 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir", 5844 + dir_s1d3)); 5845 + 5846 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5847 + EXPECT_EQ(0, records.access); 5848 + EXPECT_EQ(1, records.domain); 5849 + } 5850 + 5851 + TEST_F(audit_layout1, make_reg) 5852 + { 5853 + struct audit_records records; 5854 + 5855 + EXPECT_EQ(0, unlink(file1_s1d3)); 5856 + 5857 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5858 + .handled_access_fs = access_fs_16, 5859 + }); 5860 + 5861 + EXPECT_EQ(-1, mknod(file1_s1d3, S_IFREG | 0644, 0)); 5862 + EXPECT_EQ(EACCES, errno); 5863 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg", 5864 + dir_s1d3)); 5865 + 5866 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5867 + EXPECT_EQ(0, records.access); 5868 + EXPECT_EQ(1, records.domain); 5869 + } 5870 + 5871 + TEST_F(audit_layout1, make_sock) 5872 + { 5873 + struct audit_records records; 5874 + 5875 + EXPECT_EQ(0, unlink(file1_s1d3)); 5876 + 5877 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5878 + .handled_access_fs = access_fs_16, 5879 + }); 5880 + 5881 + EXPECT_EQ(-1, mknod(file1_s1d3, S_IFSOCK | 0644, 0)); 5882 + EXPECT_EQ(EACCES, errno); 5883 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock", 5884 + dir_s1d3)); 5885 + 5886 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5887 + EXPECT_EQ(0, records.access); 5888 + EXPECT_EQ(1, records.domain); 5889 + } 5890 + 5891 + TEST_F(audit_layout1, make_fifo) 5892 + { 5893 + struct audit_records records; 5894 + 5895 + EXPECT_EQ(0, unlink(file1_s1d3)); 5896 + 5897 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5898 + .handled_access_fs = access_fs_16, 5899 + }); 5900 + 5901 + EXPECT_EQ(-1, mknod(file1_s1d3, S_IFIFO | 0644, 0)); 5902 + EXPECT_EQ(EACCES, errno); 5903 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo", 5904 + dir_s1d3)); 5905 + 5906 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5907 + EXPECT_EQ(0, records.access); 5908 + EXPECT_EQ(1, records.domain); 5909 + } 5910 + 5911 + TEST_F(audit_layout1, make_block) 5912 + { 5913 + struct audit_records records; 5914 + 5915 + EXPECT_EQ(0, unlink(file1_s1d3)); 5916 + 5917 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5918 + .handled_access_fs = access_fs_16, 5919 + }); 5920 + 5921 + EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0)); 5922 + EXPECT_EQ(EACCES, errno); 5923 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5924 + "fs\\.make_block", dir_s1d3)); 5925 + 5926 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5927 + EXPECT_EQ(0, records.access); 5928 + EXPECT_EQ(1, records.domain); 5929 + } 5930 + 5931 + TEST_F(audit_layout1, make_sym) 5932 + { 5933 + struct audit_records records; 5934 + 5935 + EXPECT_EQ(0, unlink(file1_s1d3)); 5936 + 5937 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5938 + .handled_access_fs = access_fs_16, 5939 + }); 5940 + 5941 + EXPECT_EQ(-1, symlink("target", file1_s1d3)); 5942 + EXPECT_EQ(EACCES, errno); 5943 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym", 5944 + dir_s1d3)); 5945 + 5946 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5947 + EXPECT_EQ(0, records.access); 5948 + EXPECT_EQ(1, records.domain); 5949 + } 5950 + 5951 + TEST_F(audit_layout1, refer_handled) 5952 + { 5953 + struct audit_records records; 5954 + 5955 + EXPECT_EQ(0, unlink(file1_s1d3)); 5956 + 5957 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 5958 + .handled_access_fs = 5959 + LANDLOCK_ACCESS_FS_REFER, 5960 + }); 5961 + 5962 + EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 5963 + EXPECT_EQ(EXDEV, errno); 5964 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 5965 + dir_s1d1)); 5966 + EXPECT_EQ(0, matches_log_domain_allocated(self->audit_fd, NULL)); 5967 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 5968 + dir_s1d3)); 5969 + 5970 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5971 + EXPECT_EQ(0, records.access); 5972 + EXPECT_EQ(0, records.domain); 5973 + } 5974 + 5975 + TEST_F(audit_layout1, refer_make) 5976 + { 5977 + struct audit_records records; 5978 + 5979 + EXPECT_EQ(0, unlink(file1_s1d3)); 5980 + 5981 + drop_access_rights(_metadata, 5982 + &(struct landlock_ruleset_attr){ 5983 + .handled_access_fs = 5984 + LANDLOCK_ACCESS_FS_MAKE_REG | 5985 + LANDLOCK_ACCESS_FS_REFER, 5986 + }); 5987 + 5988 + EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 5989 + EXPECT_EQ(EACCES, errno); 5990 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 5991 + dir_s1d1)); 5992 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 5993 + "fs\\.make_reg,fs\\.refer", dir_s1d3)); 5994 + 5995 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 5996 + EXPECT_EQ(0, records.access); 5997 + EXPECT_EQ(0, records.domain); 5998 + } 5999 + 6000 + TEST_F(audit_layout1, refer_rename) 6001 + { 6002 + struct audit_records records; 6003 + 6004 + EXPECT_EQ(0, unlink(file1_s1d3)); 6005 + 6006 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 6007 + .handled_access_fs = access_fs_16, 6008 + }); 6009 + 6010 + EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3)); 6011 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 6012 + "fs\\.remove_file,fs\\.refer", dir_s1d2)); 6013 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 6014 + "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 6015 + dir_s2d3)); 6016 + 6017 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 6018 + EXPECT_EQ(0, records.access); 6019 + EXPECT_EQ(0, records.domain); 6020 + } 6021 + 6022 + TEST_F(audit_layout1, refer_exchange) 6023 + { 6024 + struct audit_records records; 6025 + 6026 + EXPECT_EQ(0, unlink(file1_s1d3)); 6027 + 6028 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 6029 + .handled_access_fs = access_fs_16, 6030 + }); 6031 + 6032 + /* 6033 + * The only difference with the previous audit_layout1.refer_rename test is 6034 + * the extra ",fs\\.make_reg" blocked by the source directory. 6035 + */ 6036 + EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3)); 6037 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 6038 + "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 6039 + dir_s1d2)); 6040 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 6041 + "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 6042 + dir_s2d3)); 6043 + 6044 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 6045 + EXPECT_EQ(0, records.access); 6046 + EXPECT_EQ(0, records.domain); 6047 + } 6048 + 6049 + /* 6050 + * This test checks that the audit record is correctly generated when the 6051 + * operation is only partially denied. This is the case for rename(2) when the 6052 + * source file is allowed to be referenced but the destination directory is not. 6053 + * 6054 + * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize 6055 + * file path walks and prepare for audit support") and commit 058518c20920 6056 + * ("landlock: Align partial refer access checks with final ones"). 6057 + */ 6058 + TEST_F(audit_layout1, refer_rename_half) 6059 + { 6060 + struct audit_records records; 6061 + const struct rule layer1[] = { 6062 + { 6063 + .path = dir_s2d2, 6064 + .access = LANDLOCK_ACCESS_FS_REFER, 6065 + }, 6066 + {}, 6067 + }; 6068 + int ruleset_fd = 6069 + create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 6070 + 6071 + ASSERT_LE(0, ruleset_fd); 6072 + enforce_ruleset(_metadata, ruleset_fd); 6073 + ASSERT_EQ(0, close(ruleset_fd)); 6074 + 6075 + ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 6076 + ASSERT_EQ(EXDEV, errno); 6077 + 6078 + /* Only half of the request is denied. */ 6079 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 6080 + dir_s1d1)); 6081 + 6082 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 6083 + EXPECT_EQ(0, records.access); 6084 + EXPECT_EQ(1, records.domain); 6085 + } 6086 + 6087 + TEST_F(audit_layout1, truncate) 6088 + { 6089 + struct audit_records records; 6090 + 6091 + drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 6092 + .handled_access_fs = access_fs_16, 6093 + }); 6094 + 6095 + EXPECT_EQ(-1, truncate(file1_s1d3, 0)); 6096 + EXPECT_EQ(EACCES, errno); 6097 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate", 6098 + file1_s1d3)); 6099 + 6100 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 6101 + EXPECT_EQ(0, records.access); 6102 + EXPECT_EQ(1, records.domain); 6103 + } 6104 + 6105 + TEST_F(audit_layout1, ioctl_dev) 6106 + { 6107 + struct audit_records records; 6108 + int fd; 6109 + 6110 + drop_access_rights(_metadata, 6111 + &(struct landlock_ruleset_attr){ 6112 + .handled_access_fs = 6113 + access_fs_16 & 6114 + ~LANDLOCK_ACCESS_FS_READ_FILE, 6115 + }); 6116 + 6117 + fd = open("/dev/null", O_RDONLY | O_CLOEXEC); 6118 + ASSERT_LE(0, fd); 6119 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 6120 + EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 6121 + "fs\\.ioctl_dev", "/dev/null", 6122 + " ioctlcmd=0x541b")); 6123 + 6124 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 6125 + EXPECT_EQ(0, records.access); 6126 + EXPECT_EQ(1, records.domain); 6127 + } 6128 + 6129 + TEST_F(audit_layout1, mount) 6130 + { 6131 + struct audit_records records; 6132 + 6133 + drop_access_rights(_metadata, 6134 + &(struct landlock_ruleset_attr){ 6135 + .handled_access_fs = 6136 + LANDLOCK_ACCESS_FS_EXECUTE, 6137 + }); 6138 + 6139 + set_cap(_metadata, CAP_SYS_ADMIN); 6140 + EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 6141 + EXPECT_EQ(EPERM, errno); 6142 + clear_cap(_metadata, CAP_SYS_ADMIN); 6143 + EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 6144 + "fs\\.change_topology", dir_s3d2)); 6145 + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 6146 + EXPECT_EQ(0, records.access); 6147 + EXPECT_EQ(1, records.domain); 5556 6148 } 5557 6149 5558 6150 TEST_HARNESS_MAIN