mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 09:16:33 +00:00
Landlock fix for v6.12-rc7
-----BEGIN PGP SIGNATURE----- iIYEABYKAC4WIQSVyBthFV4iTW/VU1/l49DojIL20gUCZy+y6BAcbWljQGRpZ2lr b2QubmV0AAoJEOXj0OiMgvbSXcAA/jrpBdfMi6MXbZkOXHw2H46j2jpBpOq67pND LqC2LA8bAP9JyiGdFF8ETch59zSa3mpKp4C/k8g/F3XwmSsqLOh3BA== =pF7T -----END PGP SIGNATURE----- Merge tag 'landlock-6.12-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux Pull landlock fixes from Mickaël Salaün: "This fixes issues in the Landlock's sandboxer sample and documentation, slightly refactors helpers (required for ongoing patch series), and improve/fix a feature merged in v6.12 (signal and abstract UNIX socket scoping)" * tag 'landlock-6.12-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: landlock: Optimize scope enforcement landlock: Refactor network access mask management landlock: Refactor filesystem access mask management samples/landlock: Clarify option parsing behaviour samples/landlock: Refactor help message samples/landlock: Fix port parsing in sandboxer landlock: Fix grammar issues in documentation landlock: Improve documentation of previous limitations
This commit is contained in:
commit
92dda329e3
@ -11,18 +11,18 @@ Landlock LSM: kernel documentation
|
||||
|
||||
Landlock's goal is to create scoped access-control (i.e. sandboxing). To
|
||||
harden a whole system, this feature should be available to any process,
|
||||
including unprivileged ones. Because such process may be compromised or
|
||||
including unprivileged ones. Because such a process may be compromised or
|
||||
backdoored (i.e. untrusted), Landlock's features must be safe to use from the
|
||||
kernel and other processes point of view. Landlock's interface must therefore
|
||||
expose a minimal attack surface.
|
||||
|
||||
Landlock is designed to be usable by unprivileged processes while following the
|
||||
system security policy enforced by other access control mechanisms (e.g. DAC,
|
||||
LSM). Indeed, a Landlock rule shall not interfere with other access-controls
|
||||
enforced on the system, only add more restrictions.
|
||||
LSM). A Landlock rule shall not interfere with other access-controls enforced
|
||||
on the system, only add more restrictions.
|
||||
|
||||
Any user can enforce Landlock rulesets on their processes. They are merged and
|
||||
evaluated according to the inherited ones in a way that ensures that only more
|
||||
evaluated against inherited rulesets in a way that ensures that only more
|
||||
constraints can be added.
|
||||
|
||||
User space documentation can be found here:
|
||||
@ -43,7 +43,7 @@ Guiding principles for safe access controls
|
||||
only impact the processes requesting them.
|
||||
* Resources (e.g. file descriptors) directly obtained from the kernel by a
|
||||
sandboxed process shall retain their scoped accesses (at the time of resource
|
||||
acquisition) whatever process use them.
|
||||
acquisition) whatever process uses them.
|
||||
Cf. `File descriptor access rights`_.
|
||||
|
||||
Design choices
|
||||
@ -71,7 +71,7 @@ the same results, when they are executed under the same Landlock domain.
|
||||
Taking the ``LANDLOCK_ACCESS_FS_TRUNCATE`` right as an example, it may be
|
||||
allowed to open a file for writing without being allowed to
|
||||
:manpage:`ftruncate` the resulting file descriptor if the related file
|
||||
hierarchy doesn't grant such access right. The following sequences of
|
||||
hierarchy doesn't grant that access right. The following sequences of
|
||||
operations have the same semantic and should then have the same result:
|
||||
|
||||
* ``truncate(path);``
|
||||
@ -81,7 +81,7 @@ Similarly to file access modes (e.g. ``O_RDWR``), Landlock access rights
|
||||
attached to file descriptors are retained even if they are passed between
|
||||
processes (e.g. through a Unix domain socket). Such access rights will then be
|
||||
enforced even if the receiving process is not sandboxed by Landlock. Indeed,
|
||||
this is required to keep a consistent access control over the whole system, and
|
||||
this is required to keep access controls consistent over the whole system, and
|
||||
this avoids unattended bypasses through file descriptor passing (i.e. confused
|
||||
deputy attack).
|
||||
|
||||
|
@ -8,13 +8,13 @@ Landlock: unprivileged access control
|
||||
=====================================
|
||||
|
||||
:Author: Mickaël Salaün
|
||||
:Date: September 2024
|
||||
:Date: October 2024
|
||||
|
||||
The goal of Landlock is to enable to restrict ambient rights (e.g. global
|
||||
The goal of Landlock is to enable restriction of ambient rights (e.g. global
|
||||
filesystem or network access) for a set of processes. Because Landlock
|
||||
is a stackable LSM, it makes possible to create safe security sandboxes as new
|
||||
security layers in addition to the existing system-wide access-controls. This
|
||||
kind of sandbox is expected to help mitigate the security impact of bugs or
|
||||
is a stackable LSM, it makes it possible to create safe security sandboxes as
|
||||
new security layers in addition to the existing system-wide access-controls.
|
||||
This kind of sandbox is expected to help mitigate the security impact of bugs or
|
||||
unexpected/malicious behaviors in user space applications. Landlock empowers
|
||||
any process, including unprivileged ones, to securely restrict themselves.
|
||||
|
||||
@ -86,8 +86,8 @@ to be explicit about the denied-by-default access rights.
|
||||
LANDLOCK_SCOPE_SIGNAL,
|
||||
};
|
||||
|
||||
Because we may not know on which kernel version an application will be
|
||||
executed, it is safer to follow a best-effort security approach. Indeed, we
|
||||
Because we may not know which kernel version an application will be executed
|
||||
on, it is safer to follow a best-effort security approach. Indeed, we
|
||||
should try to protect users as much as possible whatever the kernel they are
|
||||
using.
|
||||
|
||||
@ -129,7 +129,7 @@ version, and only use the available subset of access rights:
|
||||
LANDLOCK_SCOPE_SIGNAL);
|
||||
}
|
||||
|
||||
This enables to create an inclusive ruleset that will contain our rules.
|
||||
This enables the creation of an inclusive ruleset that will contain our rules.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@ -219,42 +219,41 @@ If the ``landlock_restrict_self`` system call succeeds, the current thread is
|
||||
now restricted and this policy will be enforced on all its subsequently created
|
||||
children as well. Once a thread is landlocked, there is no way to remove its
|
||||
security policy; only adding more restrictions is allowed. These threads are
|
||||
now in a new Landlock domain, merge of their parent one (if any) with the new
|
||||
ruleset.
|
||||
now in a new Landlock domain, which is a merger of their parent one (if any)
|
||||
with the new ruleset.
|
||||
|
||||
Full working code can be found in `samples/landlock/sandboxer.c`_.
|
||||
|
||||
Good practices
|
||||
--------------
|
||||
|
||||
It is recommended setting access rights to file hierarchy leaves as much as
|
||||
It is recommended to set access rights to file hierarchy leaves as much as
|
||||
possible. For instance, it is better to be able to have ``~/doc/`` as a
|
||||
read-only hierarchy and ``~/tmp/`` as a read-write hierarchy, compared to
|
||||
``~/`` as a read-only hierarchy and ``~/tmp/`` as a read-write hierarchy.
|
||||
Following this good practice leads to self-sufficient hierarchies that do not
|
||||
depend on their location (i.e. parent directories). This is particularly
|
||||
relevant when we want to allow linking or renaming. Indeed, having consistent
|
||||
access rights per directory enables to change the location of such directory
|
||||
access rights per directory enables changing the location of such directories
|
||||
without relying on the destination directory access rights (except those that
|
||||
are required for this operation, see ``LANDLOCK_ACCESS_FS_REFER``
|
||||
documentation).
|
||||
|
||||
Having self-sufficient hierarchies also helps to tighten the required access
|
||||
rights to the minimal set of data. This also helps avoid sinkhole directories,
|
||||
i.e. directories where data can be linked to but not linked from. However,
|
||||
i.e. directories where data can be linked to but not linked from. However,
|
||||
this depends on data organization, which might not be controlled by developers.
|
||||
In this case, granting read-write access to ``~/tmp/``, instead of write-only
|
||||
access, would potentially allow to move ``~/tmp/`` to a non-readable directory
|
||||
access, would potentially allow moving ``~/tmp/`` to a non-readable directory
|
||||
and still keep the ability to list the content of ``~/tmp/``.
|
||||
|
||||
Layers of file path access rights
|
||||
---------------------------------
|
||||
|
||||
Each time a thread enforces a ruleset on itself, it updates its Landlock domain
|
||||
with a new layer of policy. Indeed, this complementary policy is stacked with
|
||||
the potentially other rulesets already restricting this thread. A sandboxed
|
||||
thread can then safely add more constraints to itself with a new enforced
|
||||
ruleset.
|
||||
with a new layer of policy. This complementary policy is stacked with any
|
||||
other rulesets potentially already restricting this thread. A sandboxed thread
|
||||
can then safely add more constraints to itself with a new enforced ruleset.
|
||||
|
||||
One policy layer grants access to a file path if at least one of its rules
|
||||
encountered on the path grants the access. A sandboxed thread can only access
|
||||
@ -265,7 +264,7 @@ etc.).
|
||||
Bind mounts and OverlayFS
|
||||
-------------------------
|
||||
|
||||
Landlock enables to restrict access to file hierarchies, which means that these
|
||||
Landlock enables restricting access to file hierarchies, which means that these
|
||||
access rights can be propagated with bind mounts (cf.
|
||||
Documentation/filesystems/sharedsubtree.rst) but not with
|
||||
Documentation/filesystems/overlayfs.rst.
|
||||
@ -278,21 +277,21 @@ access to multiple file hierarchies at the same time, whether these hierarchies
|
||||
are the result of bind mounts or not.
|
||||
|
||||
An OverlayFS mount point consists of upper and lower layers. These layers are
|
||||
combined in a merge directory, result of the mount point. This merge hierarchy
|
||||
may include files from the upper and lower layers, but modifications performed
|
||||
on the merge hierarchy only reflects on the upper layer. From a Landlock
|
||||
policy point of view, each OverlayFS layers and merge hierarchies are
|
||||
standalone and contains their own set of files and directories, which is
|
||||
different from bind mounts. A policy restricting an OverlayFS layer will not
|
||||
restrict the resulted merged hierarchy, and vice versa. Landlock users should
|
||||
then only think about file hierarchies they want to allow access to, regardless
|
||||
of the underlying filesystem.
|
||||
combined in a merge directory, and that merged directory becomes available at
|
||||
the mount point. This merge hierarchy may include files from the upper and
|
||||
lower layers, but modifications performed on the merge hierarchy only reflect
|
||||
on the upper layer. From a Landlock policy point of view, all OverlayFS layers
|
||||
and merge hierarchies are standalone and each contains their own set of files
|
||||
and directories, which is different from bind mounts. A policy restricting an
|
||||
OverlayFS layer will not restrict the resulted merged hierarchy, and vice versa.
|
||||
Landlock users should then only think about file hierarchies they want to allow
|
||||
access to, regardless of the underlying filesystem.
|
||||
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
Every new thread resulting from a :manpage:`clone(2)` inherits Landlock domain
|
||||
restrictions from its parent. This is similar to the seccomp inheritance (cf.
|
||||
restrictions from its parent. This is similar to seccomp inheritance (cf.
|
||||
Documentation/userspace-api/seccomp_filter.rst) or any other LSM dealing with
|
||||
task's :manpage:`credentials(7)`. For instance, one process's thread may apply
|
||||
Landlock rules to itself, but they will not be automatically applied to other
|
||||
@ -311,8 +310,8 @@ Ptrace restrictions
|
||||
A sandboxed process has less privileges than a non-sandboxed process and must
|
||||
then be subject to additional restrictions when manipulating another process.
|
||||
To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target
|
||||
process, a sandboxed process should have a subset of the target process rules,
|
||||
which means the tracee must be in a sub-domain of the tracer.
|
||||
process, a sandboxed process should have a superset of the target process's
|
||||
access rights, which means the tracee must be in a sub-domain of the tracer.
|
||||
|
||||
IPC scoping
|
||||
-----------
|
||||
@ -322,7 +321,7 @@ interactions between sandboxes. Each Landlock domain can be explicitly scoped
|
||||
for a set of actions by specifying it on a ruleset. For example, if a
|
||||
sandboxed process should not be able to :manpage:`connect(2)` to a
|
||||
non-sandboxed process through abstract :manpage:`unix(7)` sockets, we can
|
||||
specify such restriction with ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET``.
|
||||
specify such a restriction with ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET``.
|
||||
Moreover, if a sandboxed process should not be able to send a signal to a
|
||||
non-sandboxed process, we can specify this restriction with
|
||||
``LANDLOCK_SCOPE_SIGNAL``.
|
||||
@ -394,7 +393,7 @@ Backward and forward compatibility
|
||||
Landlock is designed to be compatible with past and future versions of the
|
||||
kernel. This is achieved thanks to the system call attributes and the
|
||||
associated bitflags, particularly the ruleset's ``handled_access_fs``. Making
|
||||
handled access right explicit enables the kernel and user space to have a clear
|
||||
handled access rights explicit enables the kernel and user space to have a clear
|
||||
contract with each other. This is required to make sure sandboxing will not
|
||||
get stricter with a system update, which could break applications.
|
||||
|
||||
@ -563,33 +562,34 @@ always allowed when using a kernel that only supports the first or second ABI.
|
||||
Starting with the Landlock ABI version 3, it is now possible to securely control
|
||||
truncation thanks to the new ``LANDLOCK_ACCESS_FS_TRUNCATE`` access right.
|
||||
|
||||
Network support (ABI < 4)
|
||||
-------------------------
|
||||
TCP bind and connect (ABI < 4)
|
||||
------------------------------
|
||||
|
||||
Starting with the Landlock ABI version 4, it is now possible to restrict TCP
|
||||
bind and connect actions to only a set of allowed ports thanks to the new
|
||||
``LANDLOCK_ACCESS_NET_BIND_TCP`` and ``LANDLOCK_ACCESS_NET_CONNECT_TCP``
|
||||
access rights.
|
||||
|
||||
IOCTL (ABI < 5)
|
||||
---------------
|
||||
Device IOCTL (ABI < 5)
|
||||
----------------------
|
||||
|
||||
IOCTL operations could not be denied before the fifth Landlock ABI, so
|
||||
:manpage:`ioctl(2)` is always allowed when using a kernel that only supports an
|
||||
earlier ABI.
|
||||
|
||||
Starting with the Landlock ABI version 5, it is possible to restrict the use of
|
||||
:manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right.
|
||||
:manpage:`ioctl(2)` on character and block devices using the new
|
||||
``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right.
|
||||
|
||||
Abstract UNIX socket scoping (ABI < 6)
|
||||
--------------------------------------
|
||||
Abstract UNIX socket (ABI < 6)
|
||||
------------------------------
|
||||
|
||||
Starting with the Landlock ABI version 6, it is possible to restrict
|
||||
connections to an abstract :manpage:`unix(7)` socket by setting
|
||||
``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET`` to the ``scoped`` ruleset attribute.
|
||||
|
||||
Signal scoping (ABI < 6)
|
||||
------------------------
|
||||
Signal (ABI < 6)
|
||||
----------------
|
||||
|
||||
Starting with the Landlock ABI version 6, it is possible to restrict
|
||||
:manpage:`signal(7)` sending by setting ``LANDLOCK_SCOPE_SIGNAL`` to the
|
||||
@ -605,9 +605,9 @@ Build time configuration
|
||||
|
||||
Landlock was first introduced in Linux 5.13 but it must be configured at build
|
||||
time with ``CONFIG_SECURITY_LANDLOCK=y``. Landlock must also be enabled at boot
|
||||
time as the other security modules. The list of security modules enabled by
|
||||
time like other security modules. The list of security modules enabled by
|
||||
default is set with ``CONFIG_LSM``. The kernel configuration should then
|
||||
contains ``CONFIG_LSM=landlock,[...]`` with ``[...]`` as the list of other
|
||||
contain ``CONFIG_LSM=landlock,[...]`` with ``[...]`` as the list of other
|
||||
potentially useful security modules for the running system (see the
|
||||
``CONFIG_LSM`` help).
|
||||
|
||||
@ -669,7 +669,7 @@ Questions and answers
|
||||
What about user space sandbox managers?
|
||||
---------------------------------------
|
||||
|
||||
Using user space process to enforce restrictions on kernel resources can lead
|
||||
Using user space processes to enforce restrictions on kernel resources can lead
|
||||
to race conditions or inconsistent evaluations (i.e. `Incorrect mirroring of
|
||||
the OS code and state
|
||||
<https://www.ndss-symposium.org/ndss2003/traps-and-pitfalls-practical-problems-system-call-interposition-based-security-tools/>`_).
|
||||
|
@ -60,6 +60,25 @@ static inline int landlock_restrict_self(const int ruleset_fd,
|
||||
#define ENV_SCOPED_NAME "LL_SCOPED"
|
||||
#define ENV_DELIMITER ":"
|
||||
|
||||
static int str2num(const char *numstr, __u64 *num_dst)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
int err = 0;
|
||||
__u64 num;
|
||||
|
||||
errno = 0;
|
||||
num = strtoull(numstr, &endptr, 10);
|
||||
if (errno != 0)
|
||||
err = errno;
|
||||
/* Was the string empty, or not entirely parsed successfully? */
|
||||
else if ((*numstr == '\0') || (*endptr != '\0'))
|
||||
err = EINVAL;
|
||||
else
|
||||
*num_dst = num;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int parse_path(char *env_path, const char ***const path_list)
|
||||
{
|
||||
int i, num_paths = 0;
|
||||
@ -160,7 +179,6 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
|
||||
char *env_port_name, *env_port_name_next, *strport;
|
||||
struct landlock_net_port_attr net_port = {
|
||||
.allowed_access = allowed_access,
|
||||
.port = 0,
|
||||
};
|
||||
|
||||
env_port_name = getenv(env_var);
|
||||
@ -171,7 +189,17 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
|
||||
|
||||
env_port_name_next = env_port_name;
|
||||
while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) {
|
||||
net_port.port = atoi(strport);
|
||||
__u64 port;
|
||||
|
||||
if (strcmp(strport, "") == 0)
|
||||
continue;
|
||||
|
||||
if (str2num(strport, &port)) {
|
||||
fprintf(stderr, "Failed to parse port at \"%s\"\n",
|
||||
strport);
|
||||
goto out_free_name;
|
||||
}
|
||||
net_port.port = port;
|
||||
if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
|
||||
&net_port, 0)) {
|
||||
fprintf(stderr,
|
||||
@ -262,6 +290,44 @@ static bool check_ruleset_scope(const char *const env_var,
|
||||
|
||||
#define LANDLOCK_ABI_LAST 6
|
||||
|
||||
#define XSTR(s) #s
|
||||
#define STR(s) XSTR(s)
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
static const char help[] =
|
||||
"usage: " ENV_FS_RO_NAME "=\"...\" " ENV_FS_RW_NAME "=\"...\" "
|
||||
"[other environment variables] %1$s <cmd> [args]...\n"
|
||||
"\n"
|
||||
"Execute the given command in a restricted environment.\n"
|
||||
"Multi-valued settings (lists of ports, paths, scopes) are colon-delimited.\n"
|
||||
"\n"
|
||||
"Mandatory settings:\n"
|
||||
"* " ENV_FS_RO_NAME ": paths allowed to be used in a read-only way\n"
|
||||
"* " ENV_FS_RW_NAME ": paths allowed to be used in a read-write way\n"
|
||||
"\n"
|
||||
"Optional settings (when not set, their associated access check "
|
||||
"is always allowed, which is different from an empty string which "
|
||||
"means an empty list):\n"
|
||||
"* " ENV_TCP_BIND_NAME ": ports allowed to bind (server)\n"
|
||||
"* " ENV_TCP_CONNECT_NAME ": ports allowed to connect (client)\n"
|
||||
"* " ENV_SCOPED_NAME ": actions denied on the outside of the landlock domain\n"
|
||||
" - \"a\" to restrict opening abstract unix sockets\n"
|
||||
" - \"s\" to restrict sending signals\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
ENV_FS_RO_NAME "=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
|
||||
ENV_FS_RW_NAME "=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
|
||||
ENV_TCP_BIND_NAME "=\"9418\" "
|
||||
ENV_TCP_CONNECT_NAME "=\"80:443\" "
|
||||
ENV_SCOPED_NAME "=\"a:s\" "
|
||||
"%1$s bash -i\n"
|
||||
"\n"
|
||||
"This sandboxer can use Landlock features up to ABI version "
|
||||
STR(LANDLOCK_ABI_LAST) ".\n";
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
int main(const int argc, char *const argv[], char *const *const envp)
|
||||
{
|
||||
const char *cmd_path;
|
||||
@ -280,47 +346,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
|
||||
};
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr,
|
||||
"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s "
|
||||
"<cmd> [args]...\n\n",
|
||||
ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
|
||||
ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]);
|
||||
fprintf(stderr,
|
||||
"Execute a command in a restricted environment.\n\n");
|
||||
fprintf(stderr,
|
||||
"Environment variables containing paths and ports "
|
||||
"each separated by a colon:\n");
|
||||
fprintf(stderr,
|
||||
"* %s: list of paths allowed to be used in a read-only way.\n",
|
||||
ENV_FS_RO_NAME);
|
||||
fprintf(stderr,
|
||||
"* %s: list of paths allowed to be used in a read-write way.\n\n",
|
||||
ENV_FS_RW_NAME);
|
||||
fprintf(stderr,
|
||||
"Environment variables containing ports are optional "
|
||||
"and could be skipped.\n");
|
||||
fprintf(stderr,
|
||||
"* %s: list of ports allowed to bind (server).\n",
|
||||
ENV_TCP_BIND_NAME);
|
||||
fprintf(stderr,
|
||||
"* %s: list of ports allowed to connect (client).\n",
|
||||
ENV_TCP_CONNECT_NAME);
|
||||
fprintf(stderr, "* %s: list of scoped IPCs.\n",
|
||||
ENV_SCOPED_NAME);
|
||||
fprintf(stderr,
|
||||
"\nexample:\n"
|
||||
"%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
|
||||
"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
|
||||
"%s=\"9418\" "
|
||||
"%s=\"80:443\" "
|
||||
"%s=\"a:s\" "
|
||||
"%s bash -i\n\n",
|
||||
ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
|
||||
ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]);
|
||||
fprintf(stderr,
|
||||
"This sandboxer can use Landlock features "
|
||||
"up to ABI version %d.\n",
|
||||
LANDLOCK_ABI_LAST);
|
||||
fprintf(stderr, help, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -388,38 +388,22 @@ static bool is_nouser_or_private(const struct dentry *dentry)
|
||||
unlikely(IS_PRIVATE(d_backing_inode(dentry))));
|
||||
}
|
||||
|
||||
static access_mask_t
|
||||
get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain)
|
||||
{
|
||||
access_mask_t access_dom = 0;
|
||||
size_t layer_level;
|
||||
|
||||
for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
|
||||
access_dom |=
|
||||
landlock_get_raw_fs_access_mask(domain, layer_level);
|
||||
return access_dom;
|
||||
}
|
||||
|
||||
static access_mask_t
|
||||
get_handled_fs_accesses(const struct landlock_ruleset *const domain)
|
||||
{
|
||||
/* Handles all initially denied by default access rights. */
|
||||
return get_raw_handled_fs_accesses(domain) |
|
||||
return landlock_union_access_masks(domain).fs |
|
||||
LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
|
||||
}
|
||||
|
||||
static const struct landlock_ruleset *
|
||||
get_fs_domain(const struct landlock_ruleset *const domain)
|
||||
{
|
||||
if (!domain || !get_raw_handled_fs_accesses(domain))
|
||||
return NULL;
|
||||
|
||||
return domain;
|
||||
}
|
||||
static const struct access_masks any_fs = {
|
||||
.fs = ~0,
|
||||
};
|
||||
|
||||
static const struct landlock_ruleset *get_current_fs_domain(void)
|
||||
{
|
||||
return get_fs_domain(landlock_get_current_domain());
|
||||
return landlock_get_applicable_domain(landlock_get_current_domain(),
|
||||
any_fs);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1517,7 +1501,8 @@ static int hook_file_open(struct file *const file)
|
||||
access_mask_t open_access_request, full_access_request, allowed_access,
|
||||
optional_access;
|
||||
const struct landlock_ruleset *const dom =
|
||||
get_fs_domain(landlock_cred(file->f_cred)->domain);
|
||||
landlock_get_applicable_domain(
|
||||
landlock_cred(file->f_cred)->domain, any_fs);
|
||||
|
||||
if (!dom)
|
||||
return 0;
|
||||
|
@ -39,27 +39,9 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
|
||||
return err;
|
||||
}
|
||||
|
||||
static access_mask_t
|
||||
get_raw_handled_net_accesses(const struct landlock_ruleset *const domain)
|
||||
{
|
||||
access_mask_t access_dom = 0;
|
||||
size_t layer_level;
|
||||
|
||||
for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
|
||||
access_dom |= landlock_get_net_access_mask(domain, layer_level);
|
||||
return access_dom;
|
||||
}
|
||||
|
||||
static const struct landlock_ruleset *get_current_net_domain(void)
|
||||
{
|
||||
const struct landlock_ruleset *const dom =
|
||||
landlock_get_current_domain();
|
||||
|
||||
if (!dom || !get_raw_handled_net_accesses(dom))
|
||||
return NULL;
|
||||
|
||||
return dom;
|
||||
}
|
||||
static const struct access_masks any_net = {
|
||||
.net = ~0,
|
||||
};
|
||||
|
||||
static int current_check_access_socket(struct socket *const sock,
|
||||
struct sockaddr *const address,
|
||||
@ -72,7 +54,9 @@ static int current_check_access_socket(struct socket *const sock,
|
||||
struct landlock_id id = {
|
||||
.type = LANDLOCK_KEY_NET_PORT,
|
||||
};
|
||||
const struct landlock_ruleset *const dom = get_current_net_domain();
|
||||
const struct landlock_ruleset *const dom =
|
||||
landlock_get_applicable_domain(landlock_get_current_domain(),
|
||||
any_net);
|
||||
|
||||
if (!dom)
|
||||
return 0;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/refcount.h>
|
||||
@ -47,6 +48,15 @@ struct access_masks {
|
||||
access_mask_t scope : LANDLOCK_NUM_SCOPE;
|
||||
};
|
||||
|
||||
union access_masks_all {
|
||||
struct access_masks masks;
|
||||
u32 all;
|
||||
};
|
||||
|
||||
/* Makes sure all fields are covered. */
|
||||
static_assert(sizeof(typeof_member(union access_masks_all, masks)) ==
|
||||
sizeof(typeof_member(union access_masks_all, all)));
|
||||
|
||||
typedef u16 layer_mask_t;
|
||||
/* Makes sure all layers can be checked. */
|
||||
static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
|
||||
@ -260,6 +270,61 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
|
||||
refcount_inc(&ruleset->usage);
|
||||
}
|
||||
|
||||
/**
|
||||
* landlock_union_access_masks - Return all access rights handled in the
|
||||
* domain
|
||||
*
|
||||
* @domain: Landlock ruleset (used as a domain)
|
||||
*
|
||||
* Returns: an access_masks result of the OR of all the domain's access masks.
|
||||
*/
|
||||
static inline struct access_masks
|
||||
landlock_union_access_masks(const struct landlock_ruleset *const domain)
|
||||
{
|
||||
union access_masks_all matches = {};
|
||||
size_t layer_level;
|
||||
|
||||
for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
|
||||
union access_masks_all layer = {
|
||||
.masks = domain->access_masks[layer_level],
|
||||
};
|
||||
|
||||
matches.all |= layer.all;
|
||||
}
|
||||
|
||||
return matches.masks;
|
||||
}
|
||||
|
||||
/**
|
||||
* landlock_get_applicable_domain - Return @domain if it applies to (handles)
|
||||
* at least one of the access rights specified
|
||||
* in @masks
|
||||
*
|
||||
* @domain: Landlock ruleset (used as a domain)
|
||||
* @masks: access masks
|
||||
*
|
||||
* Returns: @domain if any access rights specified in @masks is handled, or
|
||||
* NULL otherwise.
|
||||
*/
|
||||
static inline const struct landlock_ruleset *
|
||||
landlock_get_applicable_domain(const struct landlock_ruleset *const domain,
|
||||
const struct access_masks masks)
|
||||
{
|
||||
const union access_masks_all masks_all = {
|
||||
.masks = masks,
|
||||
};
|
||||
union access_masks_all merge = {};
|
||||
|
||||
if (!domain)
|
||||
return NULL;
|
||||
|
||||
merge.masks = landlock_union_access_masks(domain);
|
||||
if (merge.all & masks_all.all)
|
||||
return domain;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
|
||||
const access_mask_t fs_access_mask,
|
||||
@ -295,19 +360,12 @@ landlock_add_scope_mask(struct landlock_ruleset *const ruleset,
|
||||
ruleset->access_masks[layer_level].scope |= mask;
|
||||
}
|
||||
|
||||
static inline access_mask_t
|
||||
landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
|
||||
const u16 layer_level)
|
||||
{
|
||||
return ruleset->access_masks[layer_level].fs;
|
||||
}
|
||||
|
||||
static inline access_mask_t
|
||||
landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
|
||||
const u16 layer_level)
|
||||
{
|
||||
/* Handles all initially denied by default access rights. */
|
||||
return landlock_get_raw_fs_access_mask(ruleset, layer_level) |
|
||||
return ruleset->access_masks[layer_level].fs |
|
||||
LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
|
||||
}
|
||||
|
||||
|
@ -329,7 +329,7 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
|
||||
return -ENOMSG;
|
||||
|
||||
/* Checks that allowed_access matches the @ruleset constraints. */
|
||||
mask = landlock_get_raw_fs_access_mask(ruleset, 0);
|
||||
mask = ruleset->access_masks[0].fs;
|
||||
if ((path_beneath_attr.allowed_access | mask) != mask)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -204,12 +204,17 @@ static bool is_abstract_socket(struct sock *const sock)
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct access_masks unix_scope = {
|
||||
.scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
|
||||
};
|
||||
|
||||
static int hook_unix_stream_connect(struct sock *const sock,
|
||||
struct sock *const other,
|
||||
struct sock *const newsk)
|
||||
{
|
||||
const struct landlock_ruleset *const dom =
|
||||
landlock_get_current_domain();
|
||||
landlock_get_applicable_domain(landlock_get_current_domain(),
|
||||
unix_scope);
|
||||
|
||||
/* Quick return for non-landlocked tasks. */
|
||||
if (!dom)
|
||||
@ -225,7 +230,8 @@ static int hook_unix_may_send(struct socket *const sock,
|
||||
struct socket *const other)
|
||||
{
|
||||
const struct landlock_ruleset *const dom =
|
||||
landlock_get_current_domain();
|
||||
landlock_get_applicable_domain(landlock_get_current_domain(),
|
||||
unix_scope);
|
||||
|
||||
if (!dom)
|
||||
return 0;
|
||||
@ -243,6 +249,10 @@ static int hook_unix_may_send(struct socket *const sock,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct access_masks signal_scope = {
|
||||
.scope = LANDLOCK_SCOPE_SIGNAL,
|
||||
};
|
||||
|
||||
static int hook_task_kill(struct task_struct *const p,
|
||||
struct kernel_siginfo *const info, const int sig,
|
||||
const struct cred *const cred)
|
||||
@ -256,6 +266,7 @@ static int hook_task_kill(struct task_struct *const p,
|
||||
} else {
|
||||
dom = landlock_get_current_domain();
|
||||
}
|
||||
dom = landlock_get_applicable_domain(dom, signal_scope);
|
||||
|
||||
/* Quick return for non-landlocked tasks. */
|
||||
if (!dom)
|
||||
@ -279,7 +290,8 @@ static int hook_file_send_sigiotask(struct task_struct *tsk,
|
||||
|
||||
/* Lock already held by send_sigio() and send_sigurg(). */
|
||||
lockdep_assert_held(&fown->lock);
|
||||
dom = landlock_file(fown->file)->fown_domain;
|
||||
dom = landlock_get_applicable_domain(
|
||||
landlock_file(fown->file)->fown_domain, signal_scope);
|
||||
|
||||
/* Quick return for unowned socket. */
|
||||
if (!dom)
|
||||
|
Loading…
Reference in New Issue
Block a user