8
8
*/
9
9
10
10
#define _GNU_SOURCE
11
+ #define __SANE_USERSPACE_TYPES__
12
+ #include <arpa/inet.h>
11
13
#include <errno.h>
12
14
#include <fcntl.h>
13
15
#include <linux/landlock.h>
@@ -51,7 +53,9 @@ static inline int landlock_restrict_self(const int ruleset_fd,
51
53
52
54
#define ENV_FS_RO_NAME "LL_FS_RO"
53
55
#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 ":"
55
59
56
60
static int parse_path (char * env_path , const char * * * const path_list )
57
61
{
@@ -60,13 +64,13 @@ static int parse_path(char *env_path, const char ***const path_list)
60
64
if (env_path ) {
61
65
num_paths ++ ;
62
66
for (i = 0 ; env_path [i ]; i ++ ) {
63
- if (env_path [i ] == ENV_PATH_TOKEN [0 ])
67
+ if (env_path [i ] == ENV_DELIMITER [0 ])
64
68
num_paths ++ ;
65
69
}
66
70
}
67
71
* path_list = malloc (num_paths * sizeof (* * path_list ));
68
72
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 );
70
74
71
75
return num_paths ;
72
76
}
@@ -81,8 +85,8 @@ static int parse_path(char *env_path, const char ***const path_list)
81
85
82
86
/* clang-format on */
83
87
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 )
86
90
{
87
91
int num_paths , i , ret = 1 ;
88
92
char * env_path_name ;
@@ -143,6 +147,39 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
143
147
return ret ;
144
148
}
145
149
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
+
146
183
/* clang-format off */
147
184
148
185
#define ACCESS_FS_ROUGHLY_READ ( \
@@ -166,39 +203,58 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
166
203
167
204
/* clang-format on */
168
205
169
- #define LANDLOCK_ABI_LAST 3
206
+ #define LANDLOCK_ABI_LAST 4
170
207
171
208
int main (const int argc , char * const argv [], char * const * const envp )
172
209
{
173
210
const char * cmd_path ;
174
211
char * const * cmd_argv ;
175
212
int ruleset_fd , abi ;
213
+ char * env_port_name ;
176
214
__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ ,
177
215
access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE ;
216
+
178
217
struct landlock_ruleset_attr ruleset_attr = {
179
218
.handled_access_fs = access_fs_rw ,
219
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
220
+ LANDLOCK_ACCESS_NET_CONNECT_TCP ,
180
221
};
181
222
182
223
if (argc < 2 ) {
183
224
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 ]);
186
229
fprintf (stderr ,
187
230
"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" );
190
234
fprintf (stderr ,
191
235
"* %s: list of paths allowed to be used in a read-only way.\n" ,
192
236
ENV_FS_RO_NAME );
193
237
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 " ,
195
239
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 );
196
249
fprintf (stderr ,
197
250
"\nexample:\n"
198
251
"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
199
252
"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
253
+ "%s=\"9418\" "
254
+ "%s=\"80:443\" "
200
255
"%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 ]);
202
258
fprintf (stderr ,
203
259
"This sandboxer can use Landlock features "
204
260
"up to ABI version %d.\n" ,
@@ -255,7 +311,12 @@ int main(const int argc, char *const argv[], char *const *const envp)
255
311
case 2 :
256
312
/* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
257
313
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 );
259
320
fprintf (stderr ,
260
321
"Hint: You should update the running kernel "
261
322
"to leverage Landlock features "
@@ -274,18 +335,42 @@ int main(const int argc, char *const argv[], char *const *const envp)
274
335
access_fs_ro &= ruleset_attr .handled_access_fs ;
275
336
access_fs_rw &= ruleset_attr .handled_access_fs ;
276
337
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
+
277
351
ruleset_fd =
278
352
landlock_create_ruleset (& ruleset_attr , sizeof (ruleset_attr ), 0 );
279
353
if (ruleset_fd < 0 ) {
280
354
perror ("Failed to create a ruleset" );
281
355
return 1 ;
282
356
}
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 )) {
284
362
goto err_close_ruleset ;
285
363
}
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 )) {
287
371
goto err_close_ruleset ;
288
372
}
373
+
289
374
if (prctl (PR_SET_NO_NEW_PRIVS , 1 , 0 , 0 , 0 )) {
290
375
perror ("Failed to restrict privileges" );
291
376
goto err_close_ruleset ;
0 commit comments