[PATCH v3 2/6] landlock: Implement LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET
Tingmao Wang
m at maowtm.org
Tue Feb 3 23:12:29 UTC 2026
Extend the existing abstract UNIX socket scoping to pathname sockets as
well. Basically all of the logic is reused between the two types, just
that pathname sockets scoping are controlled by another bit, and has its
own audit request type (since the current one is named
"abstract_unix_socket").
Closes: https://github.com/landlock-lsm/linux/issues/51
Signed-off-by: Tingmao Wang <m at maowtm.org>
---
Changes in v3:
- missing dot in comment
- More accurate comment on check_socket_access
- Unix -> UNIX
Changes in v2:
- Factor out common code in hook_unix_stream_connect and
hook_unix_may_send into check_socket_access(), and inline
is_abstract_socket().
security/landlock/audit.c | 4 ++
security/landlock/audit.h | 1 +
security/landlock/task.c | 113 ++++++++++++++++++++++----------------
3 files changed, 71 insertions(+), 47 deletions(-)
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index 650bd7f5cb6b..97f0f503a836 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -75,6 +75,10 @@ get_blocker(const enum landlock_request_type type,
WARN_ON_ONCE(access_bit != -1);
return "scope.abstract_unix_socket";
+ case LANDLOCK_REQUEST_SCOPE_PATHNAME_UNIX_SOCKET:
+ WARN_ON_ONCE(access_bit != -1);
+ return "scope.pathname_unix_socket";
+
case LANDLOCK_REQUEST_SCOPE_SIGNAL:
WARN_ON_ONCE(access_bit != -1);
return "scope.signal";
diff --git a/security/landlock/audit.h b/security/landlock/audit.h
index 104472060ef5..0e40fed17f21 100644
--- a/security/landlock/audit.h
+++ b/security/landlock/audit.h
@@ -21,6 +21,7 @@ enum landlock_request_type {
LANDLOCK_REQUEST_NET_ACCESS,
LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
LANDLOCK_REQUEST_SCOPE_SIGNAL,
+ LANDLOCK_REQUEST_SCOPE_PATHNAME_UNIX_SOCKET,
};
/*
diff --git a/security/landlock/task.c b/security/landlock/task.c
index 833bc0cfe5c9..973de1efc08a 100644
--- a/security/landlock/task.c
+++ b/security/landlock/task.c
@@ -232,35 +232,85 @@ static bool domain_is_scoped(const struct landlock_ruleset *const client,
return false;
}
+/**
+ * sock_is_scoped - Check if socket connect or send should be restricted
+ * based on scope controls.
+ *
+ * @other: The server socket.
+ * @domain: The client domain.
+ * @scope: The relevant scope bit to check (i.e. pathname or abstract).
+ *
+ * Returns: True if connect should be restricted, false otherwise.
+ */
static bool sock_is_scoped(struct sock *const other,
- const struct landlock_ruleset *const domain)
+ const struct landlock_ruleset *const domain,
+ access_mask_t scope)
{
const struct landlock_ruleset *dom_other;
/* The credentials will not change. */
lockdep_assert_held(&unix_sk(other)->lock);
dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
- return domain_is_scoped(domain, dom_other,
- LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+ return domain_is_scoped(domain, dom_other, scope);
}
-static bool is_abstract_socket(struct sock *const sock)
+/*
+ * Allow us to quickly test if the current domain scopes any form of
+ * socket.
+ */
+static const struct access_masks unix_scope = {
+ .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
+ LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET,
+};
+
+/*
+ * UNIX sockets can have three types of addresses: pathname (a filesystem path),
+ * unnamed (not bound to an address), and abstract (sun_path[0] is '\0').
+ * Unnamed sockets include those created with socketpair() and unbound sockets.
+ * We do not restrict unnamed sockets since they cannot be used to reach a
+ * new peer.
+ */
+static int
+check_socket_access(struct sock *const other,
+ const struct landlock_cred_security *const subject,
+ const size_t handle_layer)
{
- struct unix_address *addr = unix_sk(sock)->addr;
+ const struct unix_address *addr = unix_sk(other)->addr;
+ access_mask_t scope;
+ enum landlock_request_type request_type;
+ /* Unnamed sockets are not restricted. */
if (!addr)
- return false;
+ return 0;
+ /*
+ * Abstract and pathname UNIX sockets have separate scope and audit
+ * request type.
+ */
if (addr->len >= offsetof(struct sockaddr_un, sun_path) + 1 &&
- addr->name->sun_path[0] == '\0')
- return true;
+ addr->name->sun_path[0] == '\0') {
+ scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET;
+ request_type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET;
+ } else {
+ scope = LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET;
+ request_type = LANDLOCK_REQUEST_SCOPE_PATHNAME_UNIX_SOCKET;
+ }
- return false;
-}
+ if (!sock_is_scoped(other, subject->domain, scope))
+ return 0;
-static const struct access_masks unix_scope = {
- .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
-};
+ landlock_log_denial(subject, &(struct landlock_request) {
+ .type = request_type,
+ .audit = {
+ .type = LSM_AUDIT_DATA_NET,
+ .u.net = &(struct lsm_network_audit) {
+ .sk = other,
+ },
+ },
+ .layer_plus_one = handle_layer + 1,
+ });
+ return -EPERM;
+}
static int hook_unix_stream_connect(struct sock *const sock,
struct sock *const other,
@@ -275,23 +325,7 @@ static int hook_unix_stream_connect(struct sock *const sock,
if (!subject)
return 0;
- if (!is_abstract_socket(other))
- return 0;
-
- if (!sock_is_scoped(other, subject->domain))
- return 0;
-
- landlock_log_denial(subject, &(struct landlock_request) {
- .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
- .audit = {
- .type = LSM_AUDIT_DATA_NET,
- .u.net = &(struct lsm_network_audit) {
- .sk = other,
- },
- },
- .layer_plus_one = handle_layer + 1,
- });
- return -EPERM;
+ return check_socket_access(other, subject, handle_layer);
}
static int hook_unix_may_send(struct socket *const sock,
@@ -302,6 +336,7 @@ static int hook_unix_may_send(struct socket *const sock,
landlock_get_applicable_subject(current_cred(), unix_scope,
&handle_layer);
+ /* Quick return for non-landlocked tasks. */
if (!subject)
return 0;
@@ -312,23 +347,7 @@ static int hook_unix_may_send(struct socket *const sock,
if (unix_peer(sock->sk) == other->sk)
return 0;
- if (!is_abstract_socket(other->sk))
- return 0;
-
- if (!sock_is_scoped(other->sk, subject->domain))
- return 0;
-
- landlock_log_denial(subject, &(struct landlock_request) {
- .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
- .audit = {
- .type = LSM_AUDIT_DATA_NET,
- .u.net = &(struct lsm_network_audit) {
- .sk = other->sk,
- },
- },
- .layer_plus_one = handle_layer + 1,
- });
- return -EPERM;
+ return check_socket_access(other->sk, subject, handle_layer);
}
static const struct access_masks signal_scope = {
--
2.52.0
More information about the Linux-security-module-archive
mailing list