[PATCH v10 0/9] Landlock: IOCTL support
Günther Noack
gnoack at google.com
Sat Mar 9 07:53:11 UTC 2024
Hello!
These patches add simple ioctl(2) support to Landlock.
Objective
~~~~~~~~~
Make ioctl(2) requests restrictable with Landlock,
in a way that is useful for real-world applications.
Proposed approach
~~~~~~~~~~~~~~~~~
Introduce the LANDLOCK_ACCESS_FS_IOCTL_DEV right, which restricts the
use of ioctl(2) on block and character devices.
We attach the this access right to opened file descriptors, as we
already do for LANDLOCK_ACCESS_FS_TRUNCATE.
If LANDLOCK_ACCESS_FS_IOCTL_DEV is handled (restricted in the
ruleset), the LANDLOCK_ACCESS_FS_IOCTL_DEV right governs the use of
all device-specific IOCTL commands. We make exceptions for common and
known-harmless IOCTL commands such as FIOCLEX, FIONCLEX, FIONBIO and
FIOASYNC, as well as other IOCTL commands for regular files, which are
implemented in fs/ioctl.c. A full list of these IOCTL commands is
listed in the documentation.
I believe that this approach works for the majority of use cases, and
offers a good trade-off between complexity of the Landlock API and
implementation and flexibility when the feature is used.
Current limitations
~~~~~~~~~~~~~~~~~~~
With this patch set, ioctl(2) requests can *not* be filtered based on
file type, device number (dev_t) or on the ioctl(2) request number.
On the initial RFC patch set [1], we have reached consensus to start
with this simpler coarse-grained approach, and build additional IOCTL
restriction capabilities on top in subsequent steps.
[1] https://lore.kernel.org/linux-security-module/d4f1395c-d2d4-1860-3a02-2a0c023dd761@digikod.net/
Notable implications of this approach
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* A processes' existing open file descriptors stay unaffected
when a process enables Landlock.
This means in particular that in common scenarios,
the terminal's IOCTLs (ioctl_tty(2)) continue to work.
* ioctl(2) continues to be available for file descriptors for
non-device files. Example: Network sockets, memfd_create(2).
Examples
~~~~~~~~
Starting a sandboxed shell from $HOME with samples/landlock/sandboxer:
LL_FS_RO=/ LL_FS_RW=. ./sandboxer /bin/bash
The LANDLOCK_ACCESS_FS_IOCTL_DEV right is part of the "read-write"
rights here, so we expect that newly opened files outside of $HOME
don't work with most IOCTL commands.
* "stty" works: It probes terminal properties
* "stty </dev/tty" fails: /dev/tty can be reopened, but the IOCTL is
denied.
* "eject" fails: ioctls to use CD-ROM drive are denied.
* "ls /dev" works: It uses ioctl to get the terminal size for
columnar layout
* The text editors "vim" and "mg" work. (GNU Emacs fails because it
attempts to reopen /dev/tty.)
Unaffected IOCTL commands
~~~~~~~~~~~~~~~~~~~~~~~~~
To decide which IOCTL commands should be blanket-permitted, we went
through the list of IOCTL commands which are handled directly in
fs/ioctl.c and looked at them individually to understand what they are
about.
The following commands are permitted by Landlock unconditionally:
* FIOCLEX, FIONCLEX - these work on the file descriptor and
manipulate the close-on-exec flag (also available through
fcntl(2) with F_SETFD)
* FIONBIO, FIOASYNC - these work on the struct file and enable
nonblocking-IO and async flags (also available through
fcntl(2) with F_SETFL)
The following commands are also technically permitted by Landlock
unconditionally, but are not supported by device files. By permitting
them in Landlock on device files, we naturally return the normal error
code.
* FIOQSIZE - get the size of the opened file or directory
* FIBMAP - get the file system block numbers underlying a file
* FS_IOC_RESVSP, FS_IOC_RESVSP64, FS_IOC_UNRESVSP, FS_IOC_UNRESVSP64,
FS_IOC_ZERO_RANGE: Backwards compatibility with legacy XFS
preallocation syscalls which predate fallocate(2).
The following commands are also technically permitted by Landlock, but
they are really operating on the file system's superblock, rather than
on the file itself:
* FIFREEZE, FITHAW - work on superblock(!) to freeze/thaw the file
system. Requires CAP_SYS_ADMIN.
* FIGETBSZ - get file system blocksize
The following commands are technically permitted by Landlock:
* FS_IOC_FIEMAP - get information about file extent mapping
(c.f. https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt)
* FIDEDUPERANGE, FICLONE, FICLONERANGE - manipulating shared physical storage
between multiple files. These only work on some COW file systems, by design.
* Accessing file attributes:
* FS_IOC_GETFLAGS, FS_IOC_SETFLAGS - manipulate inode flags (ioctl_iflags(2))
* FS_IOC_FSGETXATTR, FS_IOC_FSSETXATTR - more attributes
Notably, the command FIONREAD is *not* blanket-permitted,
because it would be a device-specific implementation.
Related Work
~~~~~~~~~~~~
OpenBSD's pledge(2) [2] restricts ioctl(2) independent of the file
descriptor which is used. The implementers maintain multiple
allow-lists of predefined ioctl(2) operations required for different
application domains such as "audio", "bpf", "tty" and "inet".
OpenBSD does not guarantee backwards compatibility to the same extent
as Linux does, so it's easier for them to update these lists in later
versions. It might not be a feasible approach for Linux though.
[2] https://man.openbsd.org/OpenBSD-7.4/pledge.2
Open Questions
~~~~~~~~~~~~~~
* Is it OK to introduce the LSM file_vfs_ioctl hook?
* We may need to revise the tests that we added in V9 and before,
so of them probably don't make sense any more.
Changes
~~~~~~~
V10:
* Major change: only restrict IOCTL invocations on device files
* Rename access right to LANDLOCK_ACCESS_FS_IOCTL_DEV
* Remove the notion of synthetic access rights and IOCTL right groups
* Introduce a new LSM hook file_vfs_ioctl, which gets invoked just
before the call to f_ops->unlocked_ioctl()
* Documentation
* Various complications were removed or simplified:
* Suggestion to mount file systems as nodev is not needed any more,
as Landlock already lets users distinguish device files.
* Remarks about fscrypt were removed. The fscrypt-related IOCTLs only
applied to regular files and directories, so this patch does not affect
them any more.
* Various documentation of the IOCTL grouping approach was removed,
as it's not needed any more.
V9:
* in “landlock: Add IOCTL access right”:
* Change IOCTL group names and grouping as discussed with Mickaël.
This makes the grouping coarser, and we occasionally rely on the
underlying implementation to perform the appropriate read/write
checks.
* Group IOCTL_RW (one of READ_FILE, WRITE_FILE or READ_DIR):
FIONREAD, FIOQSIZE, FIGETBSZ
* Group IOCTL_RWF (one of READ_FILE or WRITE_FILE):
FS_IOC_FIEMAP, FIBMAP, FIDEDUPERANGE, FICLONE, FICLONERANGE,
FS_IOC_RESVSP, FS_IOC_RESVSP64, FS_IOC_UNRESVSP, FS_IOC_UNRESVSP64,
FS_IOC_ZERO_RANGE
* Excempt pipe file descriptors from IOCTL restrictions,
even for named pipes which are opened from the file system.
This is to be consistent with anonymous pipes created with pipe(2).
As discussed in https://lore.kernel.org/r/ZP7lxmXklksadvz+@google.com
* Document rationale for the IOCTL grouping in the code
* Use __attribute_const__
* Rename required_ioctl_access() to get_required_ioctl_access()
* Selftests
* Simplify IOCTL test fixtures as a result of simpler grouping.
* Test that IOCTLs are permitted on named pipe FDs.
* Test that IOCTLs are permitted on named Unix Domain Socket FDs.
* Work around compilation issue with old GCC / glibc.
https://sourceware.org/glibc/wiki/Synchronizing_Headers
Thanks to Huyadi <hu.yadi at h3c.com>, who pointed this out in
https://lore.kernel.org/all/f25be6663bcc4608adf630509f045a76@h3c.com/
and Mickaël, who fixed it through #include reordering.
* Documentation changes
* Reword "IOCTL commands" section a bit
* s/permit/allow/
* s/access right/right/, if preceded by LANDLOCK_ACCESS_FS_*
* s/IOCTL/FS_IOCTL/ in ASCII table
* Update IOCTL grouping documentation in header file
* Removed a few of the earlier commits in this patch set,
which have already been merged.
V8:
* Documentation changes
* userspace-api/landlock.rst:
* Add an extra paragraph about how the IOCTL right combines
when used with other access rights.
* Explain better the circumstances under which passing of
file descriptors between different Landlock domains can happen
* limits.h: Add comment to explain public vs internal FS access rights
* Add a paragraph in the commit to explain better why the IOCTL
right works as it does
V7:
* in “landlock: Add IOCTL access right”:
* Make IOCTL_GROUPS a #define so that static_assert works even on
old compilers (bug reported by Intel about PowerPC GCC9 config)
* Adapt indentation of IOCTL_GROUPS definition
* Add missing dots in kernel-doc comments.
* in “landlock: Remove remaining "inline" modifiers in .c files”:
* explain reasoning in commit message
V6:
* Implementation:
* Check that only publicly visible access rights can be used when adding a
rule (rather than the synthetic ones). Thanks Mickaël for spotting that!
* Move all functionality related to IOCTL groups and synthetic access rights
into the same place at the top of fs.c
* Move kernel doc to the .c file in one instance
* Smaller code style issues (upcase IOCTL, vardecl at block start)
* Remove inline modifier from functions in .c files
* Tests:
* use SKIP
* Rename 'fd' to dir_fd and file_fd where appropriate
* Remove duplicate "ioctl" mentions from test names
* Rename "permitted" to "allowed", in ioctl and ftruncate tests
* Do not add rules if access is 0, in test helper
V5:
* Implementation:
* move IOCTL group expansion logic into fs.c (implementation suggested by
mic)
* rename IOCTL_CMD_G* constants to LANDLOCK_ACCESS_FS_IOCTL_GROUP*
* fs.c: create ioctl_groups constant
* add "const" to some variables
* Formatting and docstring fixes (including wrong kernel-doc format)
* samples/landlock: fix ABI version and fallback attribute (mic)
* Documentation
* move header documentation changes into the implementation commit
* spell out how FIFREEZE, FITHAW and attribute-manipulation ioctls from
fs/ioctl.c are handled
* change ABI 4 to ABI 5 in some missing places
V4:
* use "synthetic" IOCTL access rights, as previously discussed
* testing changes
* use a large fixture-based test, for more exhaustive coverage,
and replace some of the earlier tests with it
* rebased on mic-next
V3:
* always permit the IOCTL commands FIOCLEX, FIONCLEX, FIONBIO, FIOASYNC and
FIONREAD, independent of LANDLOCK_ACCESS_FS_IOCTL
* increment ABI version in the same commit where the feature is introduced
* testing changes
* use FIOQSIZE instead of TTY IOCTL commands
(FIOQSIZE works with regular files, directories and memfds)
* run the memfd test with both Landlock enabled and disabled
* add a test for the always-permitted IOCTL commands
V2:
* rebased on mic-next
* added documentation
* exercise ioctl(2) in the memfd test
* test: Use layout0 for the test
---
V1: https://lore.kernel.org/linux-security-module/20230502171755.9788-1-gnoack3000@gmail.com/
V2: https://lore.kernel.org/linux-security-module/20230623144329.136541-1-gnoack@google.com/
V3: https://lore.kernel.org/linux-security-module/20230814172816.3907299-1-gnoack@google.com/
V4: https://lore.kernel.org/linux-security-module/20231103155717.78042-1-gnoack@google.com/
V5: https://lore.kernel.org/linux-security-module/20231117154920.1706371-1-gnoack@google.com/
V6: https://lore.kernel.org/linux-security-module/20231124173026.3257122-1-gnoack@google.com/
V7: https://lore.kernel.org/linux-security-module/20231201143042.3276833-1-gnoack@google.com/
V8: https://lore.kernel.org/linux-security-module/20231208155121.1943775-1-gnoack@google.com/
V9: https://lore.kernel.org/linux-security-module/20240209170612.1638517-1-gnoack@google.com/
Günther Noack (9):
security: Create security_file_vfs_ioctl hook
landlock: Add IOCTL access right for character and block devices
selftests/landlock: Test IOCTL support
selftests/landlock: Test IOCTL with memfds
selftests/landlock: Test ioctl(2) and ftruncate(2) with open(O_PATH)
selftests/landlock: Test IOCTLs on named pipes
selftests/landlock: Check IOCTL restrictions for named UNIX domain
sockets
samples/landlock: Add support for LANDLOCK_ACCESS_FS_IOCTL_DEV
landlock: Document IOCTL support
Documentation/userspace-api/landlock.rst | 76 +++-
fs/ioctl.c | 14 +-
include/linux/lsm_hook_defs.h | 2 +
include/linux/security.h | 8 +
include/uapi/linux/landlock.h | 35 +-
samples/landlock/sandboxer.c | 13 +-
security/landlock/fs.c | 38 +-
security/landlock/limits.h | 2 +-
security/landlock/syscalls.c | 8 +-
security/security.c | 22 +
tools/testing/selftests/landlock/base_test.c | 2 +-
tools/testing/selftests/landlock/fs_test.c | 407 ++++++++++++++++++-
12 files changed, 581 insertions(+), 46 deletions(-)
base-commit: d8482176c8c319b11d683913f780d63b44257d0f
--
2.44.0.278.ge034bb2e1d-goog
More information about the Linux-security-module-archive
mailing list