landlock: fix narrow file grants silently dropped on regular files
ober
b37dd197a895873c652e02771cf9ae816b4dbc37
--- a/support/landlock-shim.c +++ b/support/landlock-shim.c @@ -12,6 +12,7 @@ #include <sys/types.h> #include <sys/prctl.h> +#include <sys/stat.h> #include <sys/syscall.h> #include <unistd.h> #include <fcntl.h> @@ -94,6 +95,19 @@ struct landlock_net_port_attr { LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ LANDLOCK_ACCESS_FS_MAKE_SYM) +/* Access bits valid for regular (non-directory) files. Landlock rejects + * landlock_add_rule with EINVAL if any directory-only bit (READ_DIR, + * MAKE_*, REMOVE_*, REFER) appears in a rule targeting a regular file, + * silently dropping the entire grant. Mask requested access against + * this set when the target is a file. IOCTL_DEV is included because + * it is valid on character/block device files (e.g. /dev/tty). */ +#define ACCESS_FS_FILE_BITS ( \ + LANDLOCK_ACCESS_FS_EXECUTE | \ + LANDLOCK_ACCESS_FS_WRITE_FILE | \ + LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_TRUNCATE | \ + LANDLOCK_ACCESS_FS_IOCTL_DEV) + /* ========== API Functions ========== */ int jerboa_landlock_sandbox_ex(const char *packed_read, @@ -196,11 +210,20 @@ int jerboa_landlock_sandbox_ex(const char *packed_read, const char *target = resolved ? resolved : (path); \ int fd = open(target, O_PATH | O_CLOEXEC); \ if (fd >= 0) { \ + uint64_t eff = (access); \ + struct stat st; \ + if (fstat(fd, &st) == 0 && !S_ISDIR(st.st_mode)) { \ + /* Regular file (or device/socket/etc): strip dir-only bits \ + * to avoid EINVAL silently dropping the entire grant. */ \ + eff &= ACCESS_FS_FILE_BITS; \ + } \ struct landlock_path_beneath_attr pb; \ - pb.allowed_access = (access) & handled; \ + pb.allowed_access = eff & handled; \ pb.parent_fd = fd; \ - syscall(__NR_landlock_add_rule, ruleset_fd, \ - LANDLOCK_RULE_PATH_BENEATH, &pb, 0); \ + if (pb.allowed_access) { \ + syscall(__NR_landlock_add_rule, ruleset_fd, \ + LANDLOCK_RULE_PATH_BENEATH, &pb, 0); \ + } \ close(fd); \ } \ free(resolved); \