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, layer_level: 0); |
34 | |
35 | mutex_lock(&ruleset->lock); |
36 | err = landlock_insert_rule(ruleset, id, access: access_rights); |
37 | mutex_unlock(lock: &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(ruleset: 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(domain: 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 | 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 | struct landlock_id id = { |
73 | .type = LANDLOCK_KEY_NET_PORT, |
74 | }; |
75 | const struct landlock_ruleset *const dom = get_current_net_domain(); |
76 | |
77 | if (!dom) |
78 | return 0; |
79 | if (WARN_ON_ONCE(dom->num_layers < 1)) |
80 | return -EACCES; |
81 | |
82 | /* Checks if it's a (potential) TCP socket. */ |
83 | if (sock->type != SOCK_STREAM) |
84 | return 0; |
85 | |
86 | /* Checks for minimal header length to safely read sa_family. */ |
87 | if (addrlen < offsetofend(typeof(*address), sa_family)) |
88 | return -EINVAL; |
89 | |
90 | switch (address->sa_family) { |
91 | case AF_UNSPEC: |
92 | case AF_INET: |
93 | if (addrlen < sizeof(struct sockaddr_in)) |
94 | return -EINVAL; |
95 | port = ((struct sockaddr_in *)address)->sin_port; |
96 | break; |
97 | |
98 | #if IS_ENABLED(CONFIG_IPV6) |
99 | case AF_INET6: |
100 | if (addrlen < SIN6_LEN_RFC2133) |
101 | return -EINVAL; |
102 | port = ((struct sockaddr_in6 *)address)->sin6_port; |
103 | break; |
104 | #endif /* IS_ENABLED(CONFIG_IPV6) */ |
105 | |
106 | default: |
107 | return 0; |
108 | } |
109 | |
110 | /* Specific AF_UNSPEC handling. */ |
111 | if (address->sa_family == AF_UNSPEC) { |
112 | /* |
113 | * Connecting to an address with AF_UNSPEC dissolves the TCP |
114 | * association, which have the same effect as closing the |
115 | * connection while retaining the socket object (i.e., the file |
116 | * descriptor). As for dropping privileges, closing |
117 | * connections is always allowed. |
118 | * |
119 | * For a TCP access control system, this request is legitimate. |
120 | * Let the network stack handle potential inconsistencies and |
121 | * return -EINVAL if needed. |
122 | */ |
123 | if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) |
124 | return 0; |
125 | |
126 | /* |
127 | * For compatibility reason, accept AF_UNSPEC for bind |
128 | * accesses (mapped to AF_INET) only if the address is |
129 | * INADDR_ANY (cf. __inet_bind). Checking the address is |
130 | * required to not wrongfully return -EACCES instead of |
131 | * -EAFNOSUPPORT. |
132 | * |
133 | * We could return 0 and let the network stack handle these |
134 | * checks, but it is safer to return a proper error and test |
135 | * consistency thanks to kselftest. |
136 | */ |
137 | if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) { |
138 | /* addrlen has already been checked for AF_UNSPEC. */ |
139 | const struct sockaddr_in *const sockaddr = |
140 | (struct sockaddr_in *)address; |
141 | |
142 | if (sock->sk->__sk_common.skc_family != AF_INET) |
143 | return -EINVAL; |
144 | |
145 | if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY)) |
146 | return -EAFNOSUPPORT; |
147 | } |
148 | } else { |
149 | /* |
150 | * Checks sa_family consistency to not wrongfully return |
151 | * -EACCES instead of -EINVAL. Valid sa_family changes are |
152 | * only (from AF_INET or AF_INET6) to AF_UNSPEC. |
153 | * |
154 | * We could return 0 and let the network stack handle this |
155 | * check, but it is safer to return a proper error and test |
156 | * consistency thanks to kselftest. |
157 | */ |
158 | if (address->sa_family != sock->sk->__sk_common.skc_family) |
159 | return -EINVAL; |
160 | } |
161 | |
162 | id.key.data = (__force uintptr_t)port; |
163 | BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data)); |
164 | |
165 | rule = landlock_find_rule(ruleset: dom, id); |
166 | access_request = landlock_init_layer_masks( |
167 | domain: dom, access_request, layer_masks: &layer_masks, key_type: LANDLOCK_KEY_NET_PORT); |
168 | if (landlock_unmask_layers(rule, access_request, layer_masks: &layer_masks, |
169 | ARRAY_SIZE(layer_masks))) |
170 | return 0; |
171 | |
172 | return -EACCES; |
173 | } |
174 | |
175 | static int hook_socket_bind(struct socket *const sock, |
176 | struct sockaddr *const address, const int addrlen) |
177 | { |
178 | return current_check_access_socket(sock, address, addrlen, |
179 | LANDLOCK_ACCESS_NET_BIND_TCP); |
180 | } |
181 | |
182 | static int hook_socket_connect(struct socket *const sock, |
183 | struct sockaddr *const address, |
184 | const int addrlen) |
185 | { |
186 | return current_check_access_socket(sock, address, addrlen, |
187 | LANDLOCK_ACCESS_NET_CONNECT_TCP); |
188 | } |
189 | |
190 | static struct security_hook_list landlock_hooks[] __ro_after_init = { |
191 | LSM_HOOK_INIT(socket_bind, hook_socket_bind), |
192 | LSM_HOOK_INIT(socket_connect, hook_socket_connect), |
193 | }; |
194 | |
195 | __init void landlock_add_net_hooks(void) |
196 | { |
197 | security_add_hooks(hooks: landlock_hooks, ARRAY_SIZE(landlock_hooks), |
198 | lsmid: &landlock_lsmid); |
199 | } |
200 | |