Commit | Line | Data |
---|---|---|
ba84b0bf MS |
1 | // SPDX-License-Identifier: BSD-3-Clause |
2 | /* | |
a17c60e5 MS |
3 | * Simple Landlock sandbox manager able to execute a process restricted by |
4 | * user-defined file system and network access control policies. | |
ba84b0bf MS |
5 | * |
6 | * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> | |
7 | * Copyright © 2020 ANSSI | |
8 | */ | |
9 | ||
10 | #define _GNU_SOURCE | |
5e990dce KM |
11 | #define __SANE_USERSPACE_TYPES__ |
12 | #include <arpa/inet.h> | |
ba84b0bf MS |
13 | #include <errno.h> |
14 | #include <fcntl.h> | |
15 | #include <linux/landlock.h> | |
16 | #include <linux/prctl.h> | |
369b48b4 | 17 | #include <linux/socket.h> |
ba84b0bf MS |
18 | #include <stddef.h> |
19 | #include <stdio.h> | |
20 | #include <stdlib.h> | |
21 | #include <string.h> | |
22 | #include <sys/prctl.h> | |
23 | #include <sys/stat.h> | |
24 | #include <sys/syscall.h> | |
25 | #include <unistd.h> | |
369b48b4 | 26 | #include <stdbool.h> |
ba84b0bf MS |
27 | |
28 | #ifndef landlock_create_ruleset | |
81709f3d MS |
29 | static inline int |
30 | landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, | |
31 | const size_t size, const __u32 flags) | |
ba84b0bf MS |
32 | { |
33 | return syscall(__NR_landlock_create_ruleset, attr, size, flags); | |
34 | } | |
35 | #endif | |
36 | ||
37 | #ifndef landlock_add_rule | |
38 | static inline int landlock_add_rule(const int ruleset_fd, | |
81709f3d MS |
39 | const enum landlock_rule_type rule_type, |
40 | const void *const rule_attr, | |
41 | const __u32 flags) | |
ba84b0bf | 42 | { |
81709f3d MS |
43 | return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, |
44 | flags); | |
ba84b0bf MS |
45 | } |
46 | #endif | |
47 | ||
48 | #ifndef landlock_restrict_self | |
49 | static inline int landlock_restrict_self(const int ruleset_fd, | |
81709f3d | 50 | const __u32 flags) |
ba84b0bf MS |
51 | { |
52 | return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); | |
53 | } | |
54 | #endif | |
55 | ||
56 | #define ENV_FS_RO_NAME "LL_FS_RO" | |
57 | #define ENV_FS_RW_NAME "LL_FS_RW" | |
5e990dce KM |
58 | #define ENV_TCP_BIND_NAME "LL_TCP_BIND" |
59 | #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" | |
369b48b4 | 60 | #define ENV_SCOPED_NAME "LL_SCOPED" |
5e990dce | 61 | #define ENV_DELIMITER ":" |
ba84b0bf MS |
62 | |
63 | static int parse_path(char *env_path, const char ***const path_list) | |
64 | { | |
65 | int i, num_paths = 0; | |
66 | ||
67 | if (env_path) { | |
68 | num_paths++; | |
69 | for (i = 0; env_path[i]; i++) { | |
5e990dce | 70 | if (env_path[i] == ENV_DELIMITER[0]) |
ba84b0bf MS |
71 | num_paths++; |
72 | } | |
73 | } | |
74 | *path_list = malloc(num_paths * sizeof(**path_list)); | |
75 | for (i = 0; i < num_paths; i++) | |
5e990dce | 76 | (*path_list)[i] = strsep(&env_path, ENV_DELIMITER); |
ba84b0bf MS |
77 | |
78 | return num_paths; | |
79 | } | |
80 | ||
9805a722 MS |
81 | /* clang-format off */ |
82 | ||
ba84b0bf MS |
83 | #define ACCESS_FILE ( \ |
84 | LANDLOCK_ACCESS_FS_EXECUTE | \ | |
85 | LANDLOCK_ACCESS_FS_WRITE_FILE | \ | |
faeb9197 | 86 | LANDLOCK_ACCESS_FS_READ_FILE | \ |
cd13738d GN |
87 | LANDLOCK_ACCESS_FS_TRUNCATE | \ |
88 | LANDLOCK_ACCESS_FS_IOCTL_DEV) | |
ba84b0bf | 89 | |
9805a722 MS |
90 | /* clang-format on */ |
91 | ||
5e990dce KM |
92 | static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd, |
93 | const __u64 allowed_access) | |
ba84b0bf MS |
94 | { |
95 | int num_paths, i, ret = 1; | |
96 | char *env_path_name; | |
97 | const char **path_list = NULL; | |
98 | struct landlock_path_beneath_attr path_beneath = { | |
99 | .parent_fd = -1, | |
100 | }; | |
101 | ||
102 | env_path_name = getenv(env_var); | |
103 | if (!env_path_name) { | |
104 | /* Prevents users to forget a setting. */ | |
105 | fprintf(stderr, "Missing environment variable %s\n", env_var); | |
106 | return 1; | |
107 | } | |
108 | env_path_name = strdup(env_path_name); | |
109 | unsetenv(env_var); | |
110 | num_paths = parse_path(env_path_name, &path_list); | |
111 | if (num_paths == 1 && path_list[0][0] == '\0') { | |
112 | /* | |
113 | * Allows to not use all possible restrictions (e.g. use | |
114 | * LL_FS_RO without LL_FS_RW). | |
115 | */ | |
116 | ret = 0; | |
117 | goto out_free_name; | |
118 | } | |
119 | ||
120 | for (i = 0; i < num_paths; i++) { | |
121 | struct stat statbuf; | |
122 | ||
81709f3d | 123 | path_beneath.parent_fd = open(path_list[i], O_PATH | O_CLOEXEC); |
ba84b0bf MS |
124 | if (path_beneath.parent_fd < 0) { |
125 | fprintf(stderr, "Failed to open \"%s\": %s\n", | |
81709f3d | 126 | path_list[i], strerror(errno)); |
a17c60e5 | 127 | continue; |
ba84b0bf MS |
128 | } |
129 | if (fstat(path_beneath.parent_fd, &statbuf)) { | |
a17c60e5 MS |
130 | fprintf(stderr, "Failed to stat \"%s\": %s\n", |
131 | path_list[i], strerror(errno)); | |
ba84b0bf MS |
132 | close(path_beneath.parent_fd); |
133 | goto out_free_name; | |
134 | } | |
135 | path_beneath.allowed_access = allowed_access; | |
136 | if (!S_ISDIR(statbuf.st_mode)) | |
137 | path_beneath.allowed_access &= ACCESS_FILE; | |
138 | if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, | |
81709f3d MS |
139 | &path_beneath, 0)) { |
140 | fprintf(stderr, | |
141 | "Failed to update the ruleset with \"%s\": %s\n", | |
142 | path_list[i], strerror(errno)); | |
ba84b0bf MS |
143 | close(path_beneath.parent_fd); |
144 | goto out_free_name; | |
145 | } | |
146 | close(path_beneath.parent_fd); | |
147 | } | |
148 | ret = 0; | |
149 | ||
150 | out_free_name: | |
66b513b7 | 151 | free(path_list); |
ba84b0bf MS |
152 | free(env_path_name); |
153 | return ret; | |
154 | } | |
155 | ||
5e990dce KM |
156 | static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, |
157 | const __u64 allowed_access) | |
158 | { | |
159 | int ret = 1; | |
42212936 | 160 | char *env_port_name, *env_port_name_next, *strport; |
5e990dce KM |
161 | struct landlock_net_port_attr net_port = { |
162 | .allowed_access = allowed_access, | |
163 | .port = 0, | |
164 | }; | |
165 | ||
166 | env_port_name = getenv(env_var); | |
167 | if (!env_port_name) | |
168 | return 0; | |
169 | env_port_name = strdup(env_port_name); | |
170 | unsetenv(env_var); | |
171 | ||
42212936 IM |
172 | env_port_name_next = env_port_name; |
173 | while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) { | |
5e990dce KM |
174 | net_port.port = atoi(strport); |
175 | if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, | |
176 | &net_port, 0)) { | |
177 | fprintf(stderr, | |
178 | "Failed to update the ruleset with port \"%llu\": %s\n", | |
179 | net_port.port, strerror(errno)); | |
180 | goto out_free_name; | |
181 | } | |
182 | } | |
183 | ret = 0; | |
184 | ||
185 | out_free_name: | |
186 | free(env_port_name); | |
187 | return ret; | |
188 | } | |
189 | ||
369b48b4 TF |
190 | /* Returns true on error, false otherwise. */ |
191 | static bool check_ruleset_scope(const char *const env_var, | |
192 | struct landlock_ruleset_attr *ruleset_attr) | |
193 | { | |
194 | char *env_type_scope, *env_type_scope_next, *ipc_scoping_name; | |
195 | bool error = false; | |
196 | bool abstract_scoping = false; | |
f490e205 | 197 | bool signal_scoping = false; |
369b48b4 TF |
198 | |
199 | /* Scoping is not supported by Landlock ABI */ | |
f490e205 TF |
200 | if (!(ruleset_attr->scoped & |
201 | (LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL))) | |
369b48b4 TF |
202 | goto out_unset; |
203 | ||
204 | env_type_scope = getenv(env_var); | |
205 | /* Scoping is not supported by the user */ | |
206 | if (!env_type_scope || strcmp("", env_type_scope) == 0) | |
207 | goto out_unset; | |
208 | ||
209 | env_type_scope = strdup(env_type_scope); | |
210 | env_type_scope_next = env_type_scope; | |
211 | while ((ipc_scoping_name = | |
212 | strsep(&env_type_scope_next, ENV_DELIMITER))) { | |
213 | if (strcmp("a", ipc_scoping_name) == 0 && !abstract_scoping) { | |
214 | abstract_scoping = true; | |
f490e205 TF |
215 | } else if (strcmp("s", ipc_scoping_name) == 0 && |
216 | !signal_scoping) { | |
217 | signal_scoping = true; | |
369b48b4 TF |
218 | } else { |
219 | fprintf(stderr, "Unknown or duplicate scope \"%s\"\n", | |
220 | ipc_scoping_name); | |
221 | error = true; | |
222 | goto out_free_name; | |
223 | } | |
224 | } | |
225 | ||
226 | out_free_name: | |
227 | free(env_type_scope); | |
228 | ||
229 | out_unset: | |
230 | if (!abstract_scoping) | |
231 | ruleset_attr->scoped &= ~LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET; | |
f490e205 TF |
232 | if (!signal_scoping) |
233 | ruleset_attr->scoped &= ~LANDLOCK_SCOPE_SIGNAL; | |
369b48b4 TF |
234 | |
235 | unsetenv(env_var); | |
236 | return error; | |
237 | } | |
238 | ||
9805a722 MS |
239 | /* clang-format off */ |
240 | ||
ba84b0bf MS |
241 | #define ACCESS_FS_ROUGHLY_READ ( \ |
242 | LANDLOCK_ACCESS_FS_EXECUTE | \ | |
243 | LANDLOCK_ACCESS_FS_READ_FILE | \ | |
244 | LANDLOCK_ACCESS_FS_READ_DIR) | |
245 | ||
246 | #define ACCESS_FS_ROUGHLY_WRITE ( \ | |
247 | LANDLOCK_ACCESS_FS_WRITE_FILE | \ | |
248 | LANDLOCK_ACCESS_FS_REMOVE_DIR | \ | |
249 | LANDLOCK_ACCESS_FS_REMOVE_FILE | \ | |
250 | LANDLOCK_ACCESS_FS_MAKE_CHAR | \ | |
251 | LANDLOCK_ACCESS_FS_MAKE_DIR | \ | |
252 | LANDLOCK_ACCESS_FS_MAKE_REG | \ | |
253 | LANDLOCK_ACCESS_FS_MAKE_SOCK | \ | |
254 | LANDLOCK_ACCESS_FS_MAKE_FIFO | \ | |
255 | LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ | |
76b902f8 | 256 | LANDLOCK_ACCESS_FS_MAKE_SYM | \ |
faeb9197 | 257 | LANDLOCK_ACCESS_FS_REFER | \ |
cd13738d GN |
258 | LANDLOCK_ACCESS_FS_TRUNCATE | \ |
259 | LANDLOCK_ACCESS_FS_IOCTL_DEV) | |
76b902f8 | 260 | |
9805a722 MS |
261 | /* clang-format on */ |
262 | ||
369b48b4 | 263 | #define LANDLOCK_ABI_LAST 6 |
903cfe8a | 264 | |
ba84b0bf MS |
265 | int main(const int argc, char *const argv[], char *const *const envp) |
266 | { | |
267 | const char *cmd_path; | |
268 | char *const *cmd_argv; | |
76b902f8 | 269 | int ruleset_fd, abi; |
5e990dce | 270 | char *env_port_name; |
76b902f8 MS |
271 | __u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ, |
272 | access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE; | |
5e990dce | 273 | |
ba84b0bf | 274 | struct landlock_ruleset_attr ruleset_attr = { |
76b902f8 | 275 | .handled_access_fs = access_fs_rw, |
5e990dce KM |
276 | .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | |
277 | LANDLOCK_ACCESS_NET_CONNECT_TCP, | |
f490e205 TF |
278 | .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | |
279 | LANDLOCK_SCOPE_SIGNAL, | |
ba84b0bf MS |
280 | }; |
281 | ||
282 | if (argc < 2) { | |
81709f3d | 283 | fprintf(stderr, |
369b48b4 | 284 | "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s " |
5e990dce KM |
285 | "<cmd> [args]...\n\n", |
286 | ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, | |
369b48b4 | 287 | ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); |
81709f3d | 288 | fprintf(stderr, |
a17c60e5 | 289 | "Execute a command in a restricted environment.\n\n"); |
5e990dce KM |
290 | fprintf(stderr, |
291 | "Environment variables containing paths and ports " | |
292 | "each separated by a colon:\n"); | |
81709f3d MS |
293 | fprintf(stderr, |
294 | "* %s: list of paths allowed to be used in a read-only way.\n", | |
295 | ENV_FS_RO_NAME); | |
296 | fprintf(stderr, | |
5e990dce | 297 | "* %s: list of paths allowed to be used in a read-write way.\n\n", |
81709f3d | 298 | ENV_FS_RW_NAME); |
5e990dce KM |
299 | fprintf(stderr, |
300 | "Environment variables containing ports are optional " | |
301 | "and could be skipped.\n"); | |
302 | fprintf(stderr, | |
303 | "* %s: list of ports allowed to bind (server).\n", | |
304 | ENV_TCP_BIND_NAME); | |
305 | fprintf(stderr, | |
306 | "* %s: list of ports allowed to connect (client).\n", | |
307 | ENV_TCP_CONNECT_NAME); | |
369b48b4 TF |
308 | fprintf(stderr, "* %s: list of scoped IPCs.\n", |
309 | ENV_SCOPED_NAME); | |
81709f3d MS |
310 | fprintf(stderr, |
311 | "\nexample:\n" | |
a17c60e5 | 312 | "%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" " |
81709f3d | 313 | "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " |
5e990dce KM |
314 | "%s=\"9418\" " |
315 | "%s=\"80:443\" " | |
f490e205 | 316 | "%s=\"a:s\" " |
903cfe8a | 317 | "%s bash -i\n\n", |
5e990dce | 318 | ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, |
369b48b4 | 319 | ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); |
903cfe8a MS |
320 | fprintf(stderr, |
321 | "This sandboxer can use Landlock features " | |
322 | "up to ABI version %d.\n", | |
323 | LANDLOCK_ABI_LAST); | |
ba84b0bf MS |
324 | return 1; |
325 | } | |
326 | ||
76b902f8 MS |
327 | abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); |
328 | if (abi < 0) { | |
ba84b0bf MS |
329 | const int err = errno; |
330 | ||
76b902f8 | 331 | perror("Failed to check Landlock compatibility"); |
ba84b0bf MS |
332 | switch (err) { |
333 | case ENOSYS: | |
81709f3d MS |
334 | fprintf(stderr, |
335 | "Hint: Landlock is not supported by the current kernel. " | |
336 | "To support it, build the kernel with " | |
337 | "CONFIG_SECURITY_LANDLOCK=y and prepend " | |
338 | "\"landlock,\" to the content of CONFIG_LSM.\n"); | |
ba84b0bf MS |
339 | break; |
340 | case EOPNOTSUPP: | |
81709f3d MS |
341 | fprintf(stderr, |
342 | "Hint: Landlock is currently disabled. " | |
343 | "It can be enabled in the kernel configuration by " | |
344 | "prepending \"landlock,\" to the content of CONFIG_LSM, " | |
345 | "or at boot time by setting the same content to the " | |
346 | "\"lsm\" kernel parameter.\n"); | |
ba84b0bf MS |
347 | break; |
348 | } | |
349 | return 1; | |
350 | } | |
903cfe8a | 351 | |
76b902f8 | 352 | /* Best-effort security. */ |
903cfe8a MS |
353 | switch (abi) { |
354 | case 1: | |
f6e53fb2 GN |
355 | /* |
356 | * Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 | |
357 | * | |
358 | * Note: The "refer" operations (file renaming and linking | |
359 | * across different directories) are always forbidden when using | |
360 | * Landlock with ABI 1. | |
361 | * | |
362 | * If only ABI 1 is available, this sandboxer knowingly forbids | |
363 | * refer operations. | |
364 | * | |
365 | * If a program *needs* to do refer operations after enabling | |
366 | * Landlock, it can not use Landlock at ABI level 1. To be | |
367 | * compatible with different kernel versions, such programs | |
368 | * should then fall back to not restrict themselves at all if | |
369 | * the running kernel only supports ABI 1. | |
370 | */ | |
903cfe8a | 371 | ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; |
faeb9197 GN |
372 | __attribute__((fallthrough)); |
373 | case 2: | |
374 | /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */ | |
375 | ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE; | |
5e990dce KM |
376 | __attribute__((fallthrough)); |
377 | case 3: | |
378 | /* Removes network support for ABI < 4 */ | |
379 | ruleset_attr.handled_access_net &= | |
380 | ~(LANDLOCK_ACCESS_NET_BIND_TCP | | |
381 | LANDLOCK_ACCESS_NET_CONNECT_TCP); | |
cd13738d GN |
382 | __attribute__((fallthrough)); |
383 | case 4: | |
384 | /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */ | |
385 | ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; | |
386 | ||
369b48b4 TF |
387 | __attribute__((fallthrough)); |
388 | case 5: | |
f490e205 TF |
389 | /* Removes LANDLOCK_SCOPE_* for ABI < 6 */ |
390 | ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | | |
391 | LANDLOCK_SCOPE_SIGNAL); | |
903cfe8a MS |
392 | fprintf(stderr, |
393 | "Hint: You should update the running kernel " | |
394 | "to leverage Landlock features " | |
395 | "provided by ABI version %d (instead of %d).\n", | |
396 | LANDLOCK_ABI_LAST, abi); | |
397 | __attribute__((fallthrough)); | |
398 | case LANDLOCK_ABI_LAST: | |
399 | break; | |
400 | default: | |
401 | fprintf(stderr, | |
402 | "Hint: You should update this sandboxer " | |
403 | "to leverage Landlock features " | |
404 | "provided by ABI version %d (instead of %d).\n", | |
405 | abi, LANDLOCK_ABI_LAST); | |
76b902f8 | 406 | } |
903cfe8a MS |
407 | access_fs_ro &= ruleset_attr.handled_access_fs; |
408 | access_fs_rw &= ruleset_attr.handled_access_fs; | |
76b902f8 | 409 | |
5e990dce KM |
410 | /* Removes bind access attribute if not supported by a user. */ |
411 | env_port_name = getenv(ENV_TCP_BIND_NAME); | |
412 | if (!env_port_name) { | |
413 | ruleset_attr.handled_access_net &= | |
414 | ~LANDLOCK_ACCESS_NET_BIND_TCP; | |
415 | } | |
416 | /* Removes connect access attribute if not supported by a user. */ | |
417 | env_port_name = getenv(ENV_TCP_CONNECT_NAME); | |
418 | if (!env_port_name) { | |
419 | ruleset_attr.handled_access_net &= | |
420 | ~LANDLOCK_ACCESS_NET_CONNECT_TCP; | |
421 | } | |
422 | ||
369b48b4 TF |
423 | if (check_ruleset_scope(ENV_SCOPED_NAME, &ruleset_attr)) |
424 | return 1; | |
425 | ||
76b902f8 MS |
426 | ruleset_fd = |
427 | landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); | |
428 | if (ruleset_fd < 0) { | |
429 | perror("Failed to create a ruleset"); | |
430 | return 1; | |
431 | } | |
5e990dce KM |
432 | |
433 | if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) { | |
434 | goto err_close_ruleset; | |
435 | } | |
436 | if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) { | |
ba84b0bf MS |
437 | goto err_close_ruleset; |
438 | } | |
5e990dce KM |
439 | |
440 | if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd, | |
441 | LANDLOCK_ACCESS_NET_BIND_TCP)) { | |
442 | goto err_close_ruleset; | |
443 | } | |
444 | if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd, | |
445 | LANDLOCK_ACCESS_NET_CONNECT_TCP)) { | |
ba84b0bf MS |
446 | goto err_close_ruleset; |
447 | } | |
5e990dce | 448 | |
ba84b0bf MS |
449 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
450 | perror("Failed to restrict privileges"); | |
451 | goto err_close_ruleset; | |
452 | } | |
453 | if (landlock_restrict_self(ruleset_fd, 0)) { | |
454 | perror("Failed to enforce ruleset"); | |
455 | goto err_close_ruleset; | |
456 | } | |
457 | close(ruleset_fd); | |
458 | ||
459 | cmd_path = argv[1]; | |
460 | cmd_argv = argv + 1; | |
a17c60e5 | 461 | fprintf(stderr, "Executing the sandboxed command...\n"); |
ba84b0bf MS |
462 | execvpe(cmd_path, cmd_argv, envp); |
463 | fprintf(stderr, "Failed to execute \"%s\": %s\n", cmd_path, | |
81709f3d | 464 | strerror(errno)); |
ba84b0bf MS |
465 | fprintf(stderr, "Hint: access to the binary, the interpreter or " |
466 | "shared libraries may be denied.\n"); | |
467 | return 1; | |
468 | ||
469 | err_close_ruleset: | |
470 | close(ruleset_fd); | |
471 | return 1; | |
472 | } |