Skip to content

Commit fff69fb

Browse files
BoardzMasterl0kod
authored andcommitted
landlock: Support network rules with TCP bind and connect
Add network rules support in the ruleset management helpers and the landlock_create_ruleset() syscall. Extend user space API to support network actions: * Add new network access rights: LANDLOCK_ACCESS_NET_BIND_TCP and LANDLOCK_ACCESS_NET_CONNECT_TCP. * Add a new network rule type: LANDLOCK_RULE_NET_PORT tied to struct landlock_net_port_attr. The allowed_access field contains the network access rights, and the port field contains the port value according to the controlled protocol. This field can take up to a 64-bit value but the maximum value depends on the related protocol (e.g. 16-bit value for TCP). Network port is in host endianness [1]. * Add a new handled_access_net field to struct landlock_ruleset_attr that contains network access rights. * Increment the Landlock ABI version to 4. Implement socket_bind() and socket_connect() LSM hooks, which enable to control TCP socket binding and connection to specific ports. Expand access_masks_t from u16 to u32 to be able to store network access rights alongside filesystem access rights for rulesets' handled access rights. Access rights are not tied to socket file descriptors but checked at bind() or connect() call time against the caller's Landlock domain. For the filesystem, a file descriptor is a direct access to a file/data. However, for network sockets, we cannot identify for which data or peer a newly created socket will give access to. Indeed, we need to wait for a connect or bind request to identify the use case for this socket. Likewise a directory file descriptor may enable to open another file (i.e. a new data item), but this opening is also restricted by the caller's domain, not the file descriptor's access rights [2]. [1] https://lore.kernel.org/r/[email protected] [2] https://lore.kernel.org/r/[email protected] Signed-off-by: Konstantin Meskhidze <[email protected]> Link: https://lore.kernel.org/r/[email protected] [mic: Extend commit message, fix typo in comments, and specify endianness in the documentation] Co-developed-by: Mickaël Salaün <[email protected]> Signed-off-by: Mickaël Salaün <[email protected]>
1 parent 0e0fc7e commit fff69fb

File tree

11 files changed

+470
-25
lines changed

11 files changed

+470
-25
lines changed

include/uapi/linux/landlock.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ struct landlock_ruleset_attr {
3131
* this access right.
3232
*/
3333
__u64 handled_access_fs;
34+
/**
35+
* @handled_access_net: Bitmask of actions (cf. `Network flags`_)
36+
* that is handled by this ruleset and should then be forbidden if no
37+
* rule explicitly allow them.
38+
*/
39+
__u64 handled_access_net;
3440
};
3541

3642
/*
@@ -54,6 +60,11 @@ enum landlock_rule_type {
5460
* landlock_path_beneath_attr .
5561
*/
5662
LANDLOCK_RULE_PATH_BENEATH = 1,
63+
/**
64+
* @LANDLOCK_RULE_NET_PORT: Type of a &struct
65+
* landlock_net_port_attr .
66+
*/
67+
LANDLOCK_RULE_NET_PORT,
5768
};
5869

5970
/**
@@ -79,6 +90,31 @@ struct landlock_path_beneath_attr {
7990
*/
8091
} __attribute__((packed));
8192

93+
/**
94+
* struct landlock_net_port_attr - Network port definition
95+
*
96+
* Argument of sys_landlock_add_rule().
97+
*/
98+
struct landlock_net_port_attr {
99+
/**
100+
* @allowed_access: Bitmask of allowed access network for a port
101+
* (cf. `Network flags`_).
102+
*/
103+
__u64 allowed_access;
104+
/**
105+
* @port: Network port in host endianness.
106+
*
107+
* It should be noted that port 0 passed to :manpage:`bind(2)` will
108+
* bind to an available port from a specific port range. This can be
109+
* configured thanks to the ``/proc/sys/net/ipv4/ip_local_port_range``
110+
* sysctl (also used for IPv6). A Landlock rule with port 0 and the
111+
* ``LANDLOCK_ACCESS_NET_BIND_TCP`` right means that requesting to bind
112+
* on port 0 is allowed and it will automatically translate to binding
113+
* on the related port range.
114+
*/
115+
__u64 port;
116+
};
117+
82118
/**
83119
* DOC: fs_access
84120
*
@@ -189,4 +225,23 @@ struct landlock_path_beneath_attr {
189225
#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
190226
/* clang-format on */
191227

228+
/**
229+
* DOC: net_access
230+
*
231+
* Network flags
232+
* ~~~~~~~~~~~~~~~~
233+
*
234+
* These flags enable to restrict a sandboxed process to a set of network
235+
* actions. This is supported since the Landlock ABI version 4.
236+
*
237+
* TCP sockets with allowed actions:
238+
*
239+
* - %LANDLOCK_ACCESS_NET_BIND_TCP: Bind a TCP socket to a local port.
240+
* - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect an active TCP socket to
241+
* a remote port.
242+
*/
243+
/* clang-format off */
244+
#define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0)
245+
#define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
246+
/* clang-format on */
192247
#endif /* _UAPI_LINUX_LANDLOCK_H */

security/landlock/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
config SECURITY_LANDLOCK
44
bool "Landlock support"
55
depends on SECURITY
6+
select SECURITY_NETWORK
67
select SECURITY_PATH
78
help
89
Landlock is a sandboxing mechanism that enables processes to restrict

security/landlock/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
22

33
landlock-y := setup.o syscalls.o object.o ruleset.o \
44
cred.o ptrace.o fs.o
5+
6+
landlock-$(CONFIG_INET) += net.o

security/landlock/limits.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
2424
#define LANDLOCK_SHIFT_ACCESS_FS 0
2525

26+
#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_TCP
27+
#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
28+
#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
29+
#define LANDLOCK_SHIFT_ACCESS_NET LANDLOCK_NUM_ACCESS_FS
30+
2631
/* clang-format on */
2732

2833
#endif /* _SECURITY_LANDLOCK_LIMITS_H */

security/landlock/net.c

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Landlock LSM - Network management and hooks
4+
*
5+
* Copyright © 2022-2023 Huawei Tech. Co., Ltd.
6+
* Copyright © 2022-2023 Microsoft Corporation
7+
*/
8+
9+
#include <linux/in.h>
10+
#include <linux/net.h>
11+
#include <linux/socket.h>
12+
#include <net/ipv6.h>
13+
14+
#include "common.h"
15+
#include "cred.h"
16+
#include "limits.h"
17+
#include "net.h"
18+
#include "ruleset.h"
19+
20+
int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
21+
const u16 port, access_mask_t access_rights)
22+
{
23+
int err;
24+
const struct landlock_id id = {
25+
.key.data = (__force uintptr_t)htons(port),
26+
.type = LANDLOCK_KEY_NET_PORT,
27+
};
28+
29+
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
30+
31+
/* Transforms relative access rights to absolute ones. */
32+
access_rights |= LANDLOCK_MASK_ACCESS_NET &
33+
~landlock_get_net_access_mask(ruleset, 0);
34+
35+
mutex_lock(&ruleset->lock);
36+
err = landlock_insert_rule(ruleset, id, access_rights);
37+
mutex_unlock(&ruleset->lock);
38+
39+
return err;
40+
}
41+
42+
static access_mask_t
43+
get_raw_handled_net_accesses(const struct landlock_ruleset *const domain)
44+
{
45+
access_mask_t access_dom = 0;
46+
size_t layer_level;
47+
48+
for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
49+
access_dom |= landlock_get_net_access_mask(domain, layer_level);
50+
return access_dom;
51+
}
52+
53+
static const struct landlock_ruleset *get_current_net_domain(void)
54+
{
55+
const struct landlock_ruleset *const dom =
56+
landlock_get_current_domain();
57+
58+
if (!dom || !get_raw_handled_net_accesses(dom))
59+
return NULL;
60+
61+
return dom;
62+
}
63+
64+
static int current_check_access_socket(struct socket *const sock,
65+
struct sockaddr *const address,
66+
const int addrlen,
67+
const access_mask_t access_request)
68+
{
69+
__be16 port;
70+
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
71+
const struct landlock_rule *rule;
72+
access_mask_t handled_access;
73+
struct landlock_id id = {
74+
.type = LANDLOCK_KEY_NET_PORT,
75+
};
76+
const struct landlock_ruleset *const dom = get_current_net_domain();
77+
78+
if (!dom)
79+
return 0;
80+
if (WARN_ON_ONCE(dom->num_layers < 1))
81+
return -EACCES;
82+
83+
/* Checks if it's a (potential) TCP socket. */
84+
if (sock->type != SOCK_STREAM)
85+
return 0;
86+
87+
/* Checks for minimal header length to safely read sa_family. */
88+
if (addrlen < offsetofend(typeof(*address), sa_family))
89+
return -EINVAL;
90+
91+
switch (address->sa_family) {
92+
case AF_UNSPEC:
93+
case AF_INET:
94+
if (addrlen < sizeof(struct sockaddr_in))
95+
return -EINVAL;
96+
port = ((struct sockaddr_in *)address)->sin_port;
97+
break;
98+
99+
#if IS_ENABLED(CONFIG_IPV6)
100+
case AF_INET6:
101+
if (addrlen < SIN6_LEN_RFC2133)
102+
return -EINVAL;
103+
port = ((struct sockaddr_in6 *)address)->sin6_port;
104+
break;
105+
#endif /* IS_ENABLED(CONFIG_IPV6) */
106+
107+
default:
108+
return 0;
109+
}
110+
111+
/* Specific AF_UNSPEC handling. */
112+
if (address->sa_family == AF_UNSPEC) {
113+
/*
114+
* Connecting to an address with AF_UNSPEC dissolves the TCP
115+
* association, which have the same effect as closing the
116+
* connection while retaining the socket object (i.e., the file
117+
* descriptor). As for dropping privileges, closing
118+
* connections is always allowed.
119+
*
120+
* For a TCP access control system, this request is legitimate.
121+
* Let the network stack handle potential inconsistencies and
122+
* return -EINVAL if needed.
123+
*/
124+
if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
125+
return 0;
126+
127+
/*
128+
* For compatibility reason, accept AF_UNSPEC for bind
129+
* accesses (mapped to AF_INET) only if the address is
130+
* INADDR_ANY (cf. __inet_bind). Checking the address is
131+
* required to not wrongfully return -EACCES instead of
132+
* -EAFNOSUPPORT.
133+
*
134+
* We could return 0 and let the network stack handle these
135+
* checks, but it is safer to return a proper error and test
136+
* consistency thanks to kselftest.
137+
*/
138+
if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
139+
/* addrlen has already been checked for AF_UNSPEC. */
140+
const struct sockaddr_in *const sockaddr =
141+
(struct sockaddr_in *)address;
142+
143+
if (sock->sk->__sk_common.skc_family != AF_INET)
144+
return -EINVAL;
145+
146+
if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY))
147+
return -EAFNOSUPPORT;
148+
}
149+
} else {
150+
/*
151+
* Checks sa_family consistency to not wrongfully return
152+
* -EACCES instead of -EINVAL. Valid sa_family changes are
153+
* only (from AF_INET or AF_INET6) to AF_UNSPEC.
154+
*
155+
* We could return 0 and let the network stack handle this
156+
* check, but it is safer to return a proper error and test
157+
* consistency thanks to kselftest.
158+
*/
159+
if (address->sa_family != sock->sk->__sk_common.skc_family)
160+
return -EINVAL;
161+
}
162+
163+
id.key.data = (__force uintptr_t)port;
164+
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
165+
166+
rule = landlock_find_rule(dom, id);
167+
handled_access = landlock_init_layer_masks(
168+
dom, access_request, &layer_masks, LANDLOCK_KEY_NET_PORT);
169+
if (landlock_unmask_layers(rule, handled_access, &layer_masks,
170+
ARRAY_SIZE(layer_masks)))
171+
return 0;
172+
173+
return -EACCES;
174+
}
175+
176+
static int hook_socket_bind(struct socket *const sock,
177+
struct sockaddr *const address, const int addrlen)
178+
{
179+
return current_check_access_socket(sock, address, addrlen,
180+
LANDLOCK_ACCESS_NET_BIND_TCP);
181+
}
182+
183+
static int hook_socket_connect(struct socket *const sock,
184+
struct sockaddr *const address,
185+
const int addrlen)
186+
{
187+
return current_check_access_socket(sock, address, addrlen,
188+
LANDLOCK_ACCESS_NET_CONNECT_TCP);
189+
}
190+
191+
static struct security_hook_list landlock_hooks[] __ro_after_init = {
192+
LSM_HOOK_INIT(socket_bind, hook_socket_bind),
193+
LSM_HOOK_INIT(socket_connect, hook_socket_connect),
194+
};
195+
196+
__init void landlock_add_net_hooks(void)
197+
{
198+
security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
199+
LANDLOCK_NAME);
200+
}

security/landlock/net.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Landlock LSM - Network management and hooks
4+
*
5+
* Copyright © 2022-2023 Huawei Tech. Co., Ltd.
6+
*/
7+
8+
#ifndef _SECURITY_LANDLOCK_NET_H
9+
#define _SECURITY_LANDLOCK_NET_H
10+
11+
#include "common.h"
12+
#include "ruleset.h"
13+
#include "setup.h"
14+
15+
#if IS_ENABLED(CONFIG_INET)
16+
__init void landlock_add_net_hooks(void);
17+
18+
int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
19+
const u16 port, access_mask_t access_rights);
20+
#else /* IS_ENABLED(CONFIG_INET) */
21+
static inline void landlock_add_net_hooks(void)
22+
{
23+
}
24+
25+
static inline int
26+
landlock_append_net_rule(struct landlock_ruleset *const ruleset, const u16 port,
27+
access_mask_t access_rights)
28+
{
29+
return -EAFNOSUPPORT;
30+
}
31+
#endif /* IS_ENABLED(CONFIG_INET) */
32+
33+
#endif /* _SECURITY_LANDLOCK_NET_H */

0 commit comments

Comments
 (0)