88 */
99
1010#define _GNU_SOURCE
11+ #define __SANE_USERSPACE_TYPES__
12+ #include <arpa/inet.h>
1113#include <errno.h>
1214#include <fcntl.h>
1315#include <linux/landlock.h>
@@ -51,7 +53,9 @@ static inline int landlock_restrict_self(const int ruleset_fd,
5153
5254#define ENV_FS_RO_NAME "LL_FS_RO"
5355#define ENV_FS_RW_NAME "LL_FS_RW"
54- #define ENV_PATH_TOKEN ":"
56+ #define ENV_TCP_BIND_NAME "LL_TCP_BIND"
57+ #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
58+ #define ENV_DELIMITER ":"
5559
5660static int parse_path (char * env_path , const char * * * const path_list )
5761{
@@ -60,13 +64,13 @@ static int parse_path(char *env_path, const char ***const path_list)
6064 if (env_path ) {
6165 num_paths ++ ;
6266 for (i = 0 ; env_path [i ]; i ++ ) {
63- if (env_path [i ] == ENV_PATH_TOKEN [0 ])
67+ if (env_path [i ] == ENV_DELIMITER [0 ])
6468 num_paths ++ ;
6569 }
6670 }
6771 * path_list = malloc (num_paths * sizeof (* * path_list ));
6872 for (i = 0 ; i < num_paths ; i ++ )
69- (* path_list )[i ] = strsep (& env_path , ENV_PATH_TOKEN );
73+ (* path_list )[i ] = strsep (& env_path , ENV_DELIMITER );
7074
7175 return num_paths ;
7276}
@@ -81,8 +85,8 @@ static int parse_path(char *env_path, const char ***const path_list)
8185
8286/* clang-format on */
8387
84- static int populate_ruleset (const char * const env_var , const int ruleset_fd ,
85- const __u64 allowed_access )
88+ static int populate_ruleset_fs (const char * const env_var , const int ruleset_fd ,
89+ const __u64 allowed_access )
8690{
8791 int num_paths , i , ret = 1 ;
8892 char * env_path_name ;
@@ -143,6 +147,39 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
143147 return ret ;
144148}
145149
150+ static int populate_ruleset_net (const char * const env_var , const int ruleset_fd ,
151+ const __u64 allowed_access )
152+ {
153+ int ret = 1 ;
154+ char * env_port_name , * strport ;
155+ struct landlock_net_port_attr net_port = {
156+ .allowed_access = allowed_access ,
157+ .port = 0 ,
158+ };
159+
160+ env_port_name = getenv (env_var );
161+ if (!env_port_name )
162+ return 0 ;
163+ env_port_name = strdup (env_port_name );
164+ unsetenv (env_var );
165+
166+ while ((strport = strsep (& env_port_name , ENV_DELIMITER ))) {
167+ net_port .port = atoi (strport );
168+ if (landlock_add_rule (ruleset_fd , LANDLOCK_RULE_NET_PORT ,
169+ & net_port , 0 )) {
170+ fprintf (stderr ,
171+ "Failed to update the ruleset with port \"%llu\": %s\n" ,
172+ net_port .port , strerror (errno ));
173+ goto out_free_name ;
174+ }
175+ }
176+ ret = 0 ;
177+
178+ out_free_name :
179+ free (env_port_name );
180+ return ret ;
181+ }
182+
146183/* clang-format off */
147184
148185#define ACCESS_FS_ROUGHLY_READ ( \
@@ -166,39 +203,58 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
166203
167204/* clang-format on */
168205
169- #define LANDLOCK_ABI_LAST 3
206+ #define LANDLOCK_ABI_LAST 4
170207
171208int main (const int argc , char * const argv [], char * const * const envp )
172209{
173210 const char * cmd_path ;
174211 char * const * cmd_argv ;
175212 int ruleset_fd , abi ;
213+ char * env_port_name ;
176214 __u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ ,
177215 access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE ;
216+
178217 struct landlock_ruleset_attr ruleset_attr = {
179218 .handled_access_fs = access_fs_rw ,
219+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
220+ LANDLOCK_ACCESS_NET_CONNECT_TCP ,
180221 };
181222
182223 if (argc < 2 ) {
183224 fprintf (stderr ,
184- "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n" ,
185- ENV_FS_RO_NAME , ENV_FS_RW_NAME , argv [0 ]);
225+ "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
226+ "<cmd> [args]...\n\n" ,
227+ ENV_FS_RO_NAME , ENV_FS_RW_NAME , ENV_TCP_BIND_NAME ,
228+ ENV_TCP_CONNECT_NAME , argv [0 ]);
186229 fprintf (stderr ,
187230 "Launch a command in a restricted environment.\n\n" );
188- fprintf (stderr , "Environment variables containing paths, "
189- "each separated by a colon:\n" );
231+ fprintf (stderr ,
232+ "Environment variables containing paths and ports "
233+ "each separated by a colon:\n" );
190234 fprintf (stderr ,
191235 "* %s: list of paths allowed to be used in a read-only way.\n" ,
192236 ENV_FS_RO_NAME );
193237 fprintf (stderr ,
194- "* %s: list of paths allowed to be used in a read-write way.\n" ,
238+ "* %s: list of paths allowed to be used in a read-write way.\n\n " ,
195239 ENV_FS_RW_NAME );
240+ fprintf (stderr ,
241+ "Environment variables containing ports are optional "
242+ "and could be skipped.\n" );
243+ fprintf (stderr ,
244+ "* %s: list of ports allowed to bind (server).\n" ,
245+ ENV_TCP_BIND_NAME );
246+ fprintf (stderr ,
247+ "* %s: list of ports allowed to connect (client).\n" ,
248+ ENV_TCP_CONNECT_NAME );
196249 fprintf (stderr ,
197250 "\nexample:\n"
198251 "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
199252 "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
253+ "%s=\"9418\" "
254+ "%s=\"80:443\" "
200255 "%s bash -i\n\n" ,
201- ENV_FS_RO_NAME , ENV_FS_RW_NAME , argv [0 ]);
256+ ENV_FS_RO_NAME , ENV_FS_RW_NAME , ENV_TCP_BIND_NAME ,
257+ ENV_TCP_CONNECT_NAME , argv [0 ]);
202258 fprintf (stderr ,
203259 "This sandboxer can use Landlock features "
204260 "up to ABI version %d.\n" ,
@@ -255,7 +311,12 @@ int main(const int argc, char *const argv[], char *const *const envp)
255311 case 2 :
256312 /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
257313 ruleset_attr .handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE ;
258-
314+ __attribute__((fallthrough ));
315+ case 3 :
316+ /* Removes network support for ABI < 4 */
317+ ruleset_attr .handled_access_net &=
318+ ~(LANDLOCK_ACCESS_NET_BIND_TCP |
319+ LANDLOCK_ACCESS_NET_CONNECT_TCP );
259320 fprintf (stderr ,
260321 "Hint: You should update the running kernel "
261322 "to leverage Landlock features "
@@ -274,18 +335,42 @@ int main(const int argc, char *const argv[], char *const *const envp)
274335 access_fs_ro &= ruleset_attr .handled_access_fs ;
275336 access_fs_rw &= ruleset_attr .handled_access_fs ;
276337
338+ /* Removes bind access attribute if not supported by a user. */
339+ env_port_name = getenv (ENV_TCP_BIND_NAME );
340+ if (!env_port_name ) {
341+ ruleset_attr .handled_access_net &=
342+ ~LANDLOCK_ACCESS_NET_BIND_TCP ;
343+ }
344+ /* Removes connect access attribute if not supported by a user. */
345+ env_port_name = getenv (ENV_TCP_CONNECT_NAME );
346+ if (!env_port_name ) {
347+ ruleset_attr .handled_access_net &=
348+ ~LANDLOCK_ACCESS_NET_CONNECT_TCP ;
349+ }
350+
277351 ruleset_fd =
278352 landlock_create_ruleset (& ruleset_attr , sizeof (ruleset_attr ), 0 );
279353 if (ruleset_fd < 0 ) {
280354 perror ("Failed to create a ruleset" );
281355 return 1 ;
282356 }
283- if (populate_ruleset (ENV_FS_RO_NAME , ruleset_fd , access_fs_ro )) {
357+
358+ if (populate_ruleset_fs (ENV_FS_RO_NAME , ruleset_fd , access_fs_ro )) {
359+ goto err_close_ruleset ;
360+ }
361+ if (populate_ruleset_fs (ENV_FS_RW_NAME , ruleset_fd , access_fs_rw )) {
284362 goto err_close_ruleset ;
285363 }
286- if (populate_ruleset (ENV_FS_RW_NAME , ruleset_fd , access_fs_rw )) {
364+
365+ if (populate_ruleset_net (ENV_TCP_BIND_NAME , ruleset_fd ,
366+ LANDLOCK_ACCESS_NET_BIND_TCP )) {
367+ goto err_close_ruleset ;
368+ }
369+ if (populate_ruleset_net (ENV_TCP_CONNECT_NAME , ruleset_fd ,
370+ LANDLOCK_ACCESS_NET_CONNECT_TCP )) {
287371 goto err_close_ruleset ;
288372 }
373+
289374 if (prctl (PR_SET_NO_NEW_PRIVS , 1 , 0 , 0 , 0 )) {
290375 perror ("Failed to restrict privileges" );
291376 goto err_close_ruleset ;
0 commit comments